Merge remote-tracking branch 'td/master'
This commit is contained in:
commit
cd4a07a7c7
@ -64,6 +64,18 @@ function(generate_pkgconfig TARGET DESCRIPTION)
|
||||
set(LIBRARIES "Libs.private:${LIBRARIES}\n")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig")
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" CONTENT
|
||||
"prefix=${PREFIX}
|
||||
@ -72,8 +84,8 @@ Name: ${TARGET}
|
||||
Description: ${DESCRIPTION}
|
||||
Version: ${PROJECT_VERSION}
|
||||
|
||||
CFlags: -I\"\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\"
|
||||
Libs: -L\"\${prefix}/${CMAKE_INSTALL_LIBDIR}\" -l${TARGET}
|
||||
CFlags: -I\"${PKGCONFIG_INCLUDEDIR}\"
|
||||
Libs: -L\"${PKGCONFIG_LIBDIR}\" -l${TARGET}
|
||||
${REQUIRES}${LIBRARIES}")
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
|
125
CMake/GetGitRevisionDescription.cmake
Normal file
125
CMake/GetGitRevisionDescription.cmake
Normal file
@ -0,0 +1,125 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar>)
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
#
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if (__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This wil return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while (NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if (cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_git_dir_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
set(${_git_dir_var} "${git_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_CURRENT_SOURCE_DIR}" "${GIT_DIR}")
|
||||
if (_relative_to_source_dir MATCHES "^[.][.]")
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
if (GIT_DIR STREQUAL "")
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if ((NOT IS_DIRECTORY ${GIT_DIR}) AND Git_FOUND)
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (NOT out STREQUAL "")
|
||||
# If out is non-empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
if (NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if (NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
43
CMake/GetGitRevisionDescription.cmake.in
Normal file
43
CMake/GetGitRevisionDescription.cmake.in
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if (HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if (EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if (PACKED_REFS MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if (NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
@ -152,6 +152,12 @@ if (CLANG OR GCC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(TD_GIT_REFSPEC TD_GIT_COMMIT_HASH)
|
||||
message(STATUS "Git state: ${TD_GIT_COMMIT_HASH}")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/td/telegram/GitCommitHash.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/td/telegram/GitCommitHash.cpp" @ONLY)
|
||||
|
||||
add_subdirectory(tdtl)
|
||||
|
||||
add_subdirectory(tdutils)
|
||||
@ -567,6 +573,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/FullMessageId.h
|
||||
td/telegram/Game.h
|
||||
td/telegram/GameManager.h
|
||||
td/telegram/GitCommitHash.h
|
||||
td/telegram/Global.h
|
||||
td/telegram/GroupCallId.h
|
||||
td/telegram/GroupCallManager.h
|
||||
@ -738,6 +745,8 @@ set(TDLIB_SOURCE
|
||||
td/telegram/VoiceNotesManager.hpp
|
||||
|
||||
${TL_TD_SCHEME_SOURCE}
|
||||
|
||||
${CMAKE_CURRENT_BINARY_DIR}/td/telegram/GitCommitHash.cpp
|
||||
)
|
||||
|
||||
set(MEMPROF_SOURCE
|
||||
@ -750,22 +759,6 @@ set(MEMPROF_STAT_SOURCE
|
||||
memprof/memprof_stat.h
|
||||
)
|
||||
|
||||
#RULES
|
||||
|
||||
file(MAKE_DIRECTORY auto)
|
||||
|
||||
if (CMAKE_HOST_WIN32)
|
||||
set(GIT_COMMIT_CMD powershell -ExecutionPolicy ByPass ./gen_git_commit_h.ps1)
|
||||
else()
|
||||
set(GIT_COMMIT_CMD ./gen_git_commit_h.sh)
|
||||
endif()
|
||||
|
||||
add_custom_target(git_commit ALL
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND ${GIT_COMMIT_CMD}
|
||||
COMMENT "Generate git_commit.h"
|
||||
)
|
||||
|
||||
#LIBRARIES
|
||||
|
||||
# memprof - simple library for memory usage profiling
|
||||
|
@ -133,7 +133,7 @@ Or you could install `TDLib` and then reference it in your CMakeLists.txt like t
|
||||
find_package(Td 1.8.4 REQUIRED)
|
||||
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||
```
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/tree/master/example/cpp/CMakeLists.txt).
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/CMakeLists.txt).
|
||||
|
||||
<a name="using-java"></a>
|
||||
## Using in Java projects
|
||||
|
@ -260,6 +260,7 @@ class QueryBench final : public td::Benchmark {
|
||||
} else if (type == 4) {
|
||||
int val = 0;
|
||||
send_lambda(client_, [&] { val = n_ * n_; });
|
||||
CHECK(val == n_ * n_);
|
||||
} else if (type == 5) {
|
||||
send_closure(client_, &ClientActor::f_promise,
|
||||
td::PromiseCreator::lambda([actor_id = actor_id(this), n = n_](td::Unit) {
|
||||
|
@ -51,7 +51,7 @@ class Backoff {
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
td::usleep_for(1);
|
||||
return cnt < 500;
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +246,7 @@
|
||||
<option>Ubuntu 16</option>
|
||||
<option>Ubuntu 18</option>
|
||||
<option>Ubuntu 20</option>
|
||||
<option>Ubuntu 22</option>
|
||||
<option>Other</option>
|
||||
</select>
|
||||
<p></p>
|
||||
@ -638,7 +639,7 @@ function onOptionsChanged() {
|
||||
var use_vcpkg = os_windows;
|
||||
|
||||
var use_lto = false;
|
||||
if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Other')))) {
|
||||
if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22' || linux_distro === 'Other')))) {
|
||||
document.getElementById('buildLtoDiv').style.display = 'block';
|
||||
use_lto = document.getElementById('buildLtoCheckbox').checked;
|
||||
} else {
|
||||
@ -797,6 +798,8 @@ function onOptionsChanged() {
|
||||
return '-6.0';
|
||||
case 'Ubuntu 20':
|
||||
return '-10';
|
||||
case 'Ubuntu 22':
|
||||
return '-14';
|
||||
default:
|
||||
return ''; // use default version
|
||||
}
|
||||
@ -849,6 +852,7 @@ function onOptionsChanged() {
|
||||
case 'Ubuntu 16':
|
||||
case 'Ubuntu 18':
|
||||
case 'Ubuntu 20':
|
||||
case 'Ubuntu 22':
|
||||
if (linux_distro.includes('Debian') && !use_root) {
|
||||
commands.push('su -');
|
||||
}
|
||||
@ -873,7 +877,7 @@ function onOptionsChanged() {
|
||||
}
|
||||
if (use_clang) {
|
||||
packages += ' clang' + getClangVersionSuffix() + ' libc++-dev';
|
||||
if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
|
||||
if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22') {
|
||||
packages += ' libc++abi-dev';
|
||||
}
|
||||
} else {
|
||||
|
@ -46,16 +46,15 @@ Convenient Python wrappers already exist for our JSON interface.
|
||||
|
||||
If you use Python >= 3.6, take a look at [python-telegram](https://github.com/alexander-akhmetov/python-telegram).
|
||||
The wrapper uses the full power of asyncio, has a good documentation and has several examples. It can be installed through pip or used in a Docker container.
|
||||
You can also try a fork [python-telegram](https://github.com/iTeam-co/python-telegram) of this library.
|
||||
You can also try a fork [python-telegram](https://github.com/iTeam-co/pytglib) of this library.
|
||||
|
||||
If you want to use TDLib with asyncio and Python >= 3.9, take a look at [aiotdlib](https://github.com/pylakey/aiotdlib).
|
||||
This wrapper contains automatically generated fully-documented classes for all TDLib API types and functions and provides set of helper methods which makes work with TDLib much simpler.
|
||||
If you want to use TDLib with asyncio and Python >= 3.9, take a look at [aiotdlib](https://github.com/pylakey/aiotdlib) or [Pytdbot](https://github.com/pytdbot/client).
|
||||
|
||||
For older Python versions you can use [pytdlib](https://github.com/pytdlib/pytdlib).
|
||||
This wrapper contains generator for TDLib API classes and basic interface for interaction with TDLib.
|
||||
|
||||
You can also check out [example/python/tdjson_example.py](https://github.com/tdlight-team/tdlight/tree/master/example/python/tdjson_example.py) and
|
||||
[tdlib-python](https://github.com/JunaidBabu/tdlib-python) for some very basic examples of TDLib JSON interface integration with Python.
|
||||
You can also check out [example/python/tdjson_example.py](https://github.com/tdlight-team/tdlight/blob/master/example/python/tdjson_example.py),
|
||||
[tdlib-python](https://github.com/JunaidBabu/tdlib-python), or [Python Wrapper TDLib](https://github.com/alvhix/pywtdlib) for some basic examples of TDLib JSON interface integration with Python.
|
||||
|
||||
<a name="javascript"></a>
|
||||
## Using TDLib in JavaScript projects
|
||||
@ -63,6 +62,8 @@ You can also check out [example/python/tdjson_example.py](https://github.com/tdl
|
||||
TDLib can be compiled to WebAssembly or asm.js and used in a browser from JavaScript. See [tdweb](https://github.com/tdlight-team/tdlight/tree/master/example/web) as a convenient wrapper for TDLib in a browser
|
||||
and [telegram-react](https://github.com/evgeny-nadymov/telegram-react) as an example of a TDLib-based Telegram client.
|
||||
|
||||
See also [Svelte-tdweb-starter](https://github.com/gennadypolakov/svelte-tdweb-starter) - Svelte wrapper for tdweb, and [Telegram-Photoframe](https://github.com/lukefx/telegram-photoframe) - a web application that displays your prefered group or channel as Photoframe.
|
||||
|
||||
TDLib can be used from Node.js through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
Convenient Node.js wrappers already exist for our JSON interface.
|
||||
@ -70,7 +71,9 @@ For example, take a look at [Airgram](https://github.com/airgram/airgram) – mo
|
||||
at [tdl](https://github.com/Bannerets/tdl), which provides a convenient, fully-asynchronous interface for interaction with TDLib and contains a bunch of examples.
|
||||
|
||||
You can also see [TdNode](https://github.com/puppy0cam/TdNode), [tglib](https://github.com/nodegin/tglib), [node-tdlib](https://github.com/wfjsw/node-tdlib), [tdlnode](https://github.com/fonbah/tdlnode),
|
||||
[Paper Plane](https://github.com/BlackSuited/paper-plane), or [node-tlg](https://github.com/dilongfa/node-tlg) for other examples of TDLib JSON interface integration with Node.js.
|
||||
[Paper Plane](https://github.com/par6n/paper-plane), or [node-tlg](https://github.com/dilongfa/node-tlg) for other examples of TDLib JSON interface integration with Node.js.
|
||||
|
||||
See also the source code of [DIBgram](https://github.com/DIBgram/DIBgram) - an unofficial Telegram web application which looks like Telegram Desktop.
|
||||
|
||||
TDLib can be used also from NativeScript through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
See [nativescript-tglib](https://github.com/arpit2438735/nativescript-tglib) as an example of a NativeScript library for building Telegram clients.
|
||||
@ -94,8 +97,6 @@ We provide a generator for JNI bridge methods and Java classes for all TDLib API
|
||||
See [example/java](https://github.com/tdlight-team/tdlight/tree/master/example/java) for an example of using TDLib from desktop Java along with detailed building and usage instructions.
|
||||
To use TDLib to create Android Java applications, use our [prebuilt library for Android](https://core.telegram.org/tdlib/tdlib.zip).
|
||||
|
||||
You can also see [JTDLib](https://github.com/ErnyTech/JTDLib) for another example of Java wrapper for TDLib.
|
||||
|
||||
<a name="kotlin"></a>
|
||||
## Using TDLib in Kotlin projects
|
||||
|
||||
@ -103,8 +104,7 @@ TDLib can be used from the Kotlin/JVM programming language through same way as i
|
||||
|
||||
You can also use [ktd](https://github.com/whyoleg/ktd) library with Kotlin-specific bindings.
|
||||
|
||||
See also [KtLib](https://github.com/nekohasekai/KtLib), which provides an automatically generated classes for TDLib API, and
|
||||
[td-ktx](https://github.com/tdlibx/td-ktx) - Kotlin coroutines wrapper for TDLib.
|
||||
See also [td-ktx](https://github.com/tdlibx/td-ktx) - Kotlin coroutines wrapper for TDLib.
|
||||
|
||||
<a name="csharp"></a>
|
||||
## Using TDLib in C# projects
|
||||
@ -117,6 +117,8 @@ See [example/csharp](https://github.com/tdlight-team/tdlight/tree/master/example
|
||||
If you want to write a cross-platform C# application using .NET Core, see [tdsharp](https://github.com/egramtel/tdsharp). It uses our [JSON](https://github.com/tdlib/td#using-json) interface,
|
||||
provides an asynchronous interface for interaction with TDLib, automatically generated classes for TDLib API and has some examples.
|
||||
|
||||
You can also use [TDLibCore](https://github.com/ph09nix/TDLibCore) library.
|
||||
|
||||
Also see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featured client rewritten from scratch in C# using TDLib SDK for Universal Windows Platform in less than 2 months,
|
||||
[egram.tel](https://github.com/egramtel/egram.tel) – a cross-platform Telegram client written in C#, .NET Core, ReactiveUI and Avalonia, or
|
||||
[telewear](https://github.com/telewear/telewear) - a Telegram client for Samsung watches.
|
||||
@ -127,10 +129,12 @@ Also see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featu
|
||||
TDLib has a simple and convenient C++11-interface for sending and receiving requests and can be statically linked to your application.
|
||||
|
||||
See [example/cpp](https://github.com/tdlight-team/tdlight/tree/master/example/cpp) for an example of TDLib usage from C++.
|
||||
[td_example.cpp](https://github.com/tdlight-team/tdlight/tree/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
|
||||
[td_example.cpp](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
|
||||
|
||||
See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbour-fernschreiber) and [Depecher](https://github.com/blacksailer/depecher) – Telegram apps for Sailfish OS,
|
||||
[TELEports](https://gitlab.com/ubports/apps/teleports) – a Qt-client for Ubuntu Touch, or [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin, all of which are based on TDLib.
|
||||
[TELEports](https://gitlab.com/ubports/development/apps/teleports) – a Qt-client for Ubuntu Touch, [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin,
|
||||
or [MeeGram](https://github.com/qtinsider/meegram2) - a Telegram client for Nokia N9,
|
||||
[TDLib Native Sciter Extension](https://github.com/RadRussianRus/TDLibNSE) - a Sciter native extension for TDLib's JSON interface, all of which are based on TDLib.
|
||||
|
||||
<a name="swift"></a>
|
||||
## Using TDLib in Swift projects
|
||||
@ -141,6 +145,8 @@ See [example/ios](https://github.com/tdlight-team/tdlight/tree/master/example/io
|
||||
|
||||
See [TDLibKit](https://github.com/Swiftgram/TDLibKit), [tdlib-swift](https://github.com/modestman/tdlib-swift), or [TDLib-iOS](https://github.com/leoMehlig/TDLib-iOS), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See also the source code of [Moc](https://github.com/ggoraa/moc) - a native and powerful macOS and iPadOS Telegram client, optimized for moderating large communities and personal use.
|
||||
|
||||
See [example/swift](https://github.com/tdlight-team/tdlight/tree/master/example/swift) for an example of a macOS Swift application.
|
||||
|
||||
<a name="objective-c"></a>
|
||||
@ -164,9 +170,12 @@ See [tdlib-lazarus](https://github.com/dieletro/tdlib-lazarus) for an example of
|
||||
|
||||
TDLib can be used from the Dart programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and a Dart Native Extension or Dart FFI.
|
||||
|
||||
See [dart_tdlib](https://github.com/periodicaidan/dart_tdlib) or [Dart wrapper for TDLib](https://github.com/tdlight-team/tdlight/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49) for an example of a TDLib Dart bindings through FFI.
|
||||
See [dart_tdlib](https://github.com/periodicaidan/dart_tdlib), [flutter_libtdjson](https://github.com/up9cloud/flutter_libtdjson), [Dart wrapper for TDLib](https://github.com/tdlib/td/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49), or [tdlib_bindings](https://github.com/lesnitsky/tdlib_bindings) for an example of a TDLib Dart bindings through FFI.
|
||||
|
||||
See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib), [tdlib-dart](https://github.com/drewpayment/tdlib-dart), [tdlib-dart](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
|
||||
See [Telegram Client library](https://github.com/azkadev/telegram_client), [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib),
|
||||
[tdlib-dart](https://github.com/drewpayment/tdlib-dart), [FluGram](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
|
||||
|
||||
See also [f-Telegram](https://github.com/evgfilim1/ftg) - Flutter Telegram client.
|
||||
|
||||
<a name="rust"></a>
|
||||
## Using TDLib in Rust projects
|
||||
@ -176,8 +185,7 @@ TDLib can be used from the Rust programming language through the [JSON](https://
|
||||
See [rust-tdlib](https://github.com/aCLr/rust-tdlib), [tdlib](https://github.com/melix99/tdlib-rs), or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures),
|
||||
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), or
|
||||
[tdjson-rs](https://github.com/mersinvald/tdjson-rs) for examples of TDLib Rust bindings.
|
||||
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), [tdjson-rs](https://github.com/mersinvald/tdjson-rs), [rust-tdlib](https://github.com/vhaoran/rust-tdlib), or [tdlib-json-sys](https://github.com/aykxt/tdlib-json-sys) for examples of TDLib Rust bindings.
|
||||
|
||||
Also see [Telegrand](https://github.com/melix99/telegrand) – a Telegram client optimized for the GNOME desktop.
|
||||
|
||||
@ -191,7 +199,7 @@ See [erl-tdlib](https://github.com/lattenwald/erl-tdlib) for an example of TDLib
|
||||
<a name="php"></a>
|
||||
## Using TDLib in PHP projects
|
||||
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib) - an FFI-based TDLib wrapper.
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib), or [tdlib-php-ffi](https://github.com/thisismzm/tdlib-php-ffi) - FFI-based TDLib wrappers.
|
||||
|
||||
See also [tdlib-schema](https://github.com/aurimasniekis/php-tdlib-schema) - a generator for TDLib API classes.
|
||||
|
||||
@ -224,7 +232,7 @@ See [d-tdlib-service](https://github.com/Lord-Evil/d-tdlib-service) for an examp
|
||||
|
||||
TDLib can be used from the Ruby programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [tdlib-ruby](https://github.com/centosadmin/tdlib-ruby) for examples of Ruby bindings and a client for TDLib.
|
||||
See [tdlib-ruby](https://github.com/southbridgeio/tdlib-ruby) for examples of Ruby bindings and a client for TDLib.
|
||||
|
||||
<a name="Crystal"></a>
|
||||
## Using TDLib in Crystal projects
|
||||
@ -267,21 +275,23 @@ See [telega.el](https://github.com/zevlg/telega.el) for an example of a GNU Emac
|
||||
|
||||
TDLib can be used from the Elixir programming language.
|
||||
|
||||
See [Elixir TDLib](https://gitlab.com/Fnux/elixir-tdlib) or [Elixir TDLib](https://github.com/QuantLayer/elixir-tdlib) for examples of such usage and an Elixir client for TDLib.
|
||||
These libraries contain automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
See [Elixir TDLib](https://github.com/QuantLayer/elixir-tdlib) for an example of such usage and an Elixir client for TDLib.
|
||||
The library contains automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
<a name="1s"></a>
|
||||
## Using TDLib from 1С:Enterprise
|
||||
|
||||
TDLib can be used from the 1С programming language.
|
||||
|
||||
See [TDLib bindings for 1С:Enterprise](https://github.com/Infactum/telegram-native) for examples of such usage.
|
||||
See [TDLib bindings for 1С:Enterprise](https://github.com/Infactum/telegram-native) and [e1c.tAgents](https://github.com/fedbka/e1c.tAgents) for examples of such usage.
|
||||
|
||||
<a name="c"></a>
|
||||
## Using TDLib in C projects
|
||||
|
||||
TDLib can be used from the C programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
|
||||
|
||||
See [easy-tg](https://github.com/Trumeet/easy-tg) for an example of such usage.
|
||||
|
||||
You can also try to use our [C](https://github.com/tdlight-team/tdlight/blob/master/td/telegram/td_c_client.h) client, which was used by the private TDLib-based version of [telegram-cli](https://github.com/vysheng/tg).
|
||||
|
||||
<a name="g"></a>
|
||||
|
@ -59,7 +59,7 @@ set(JAVA_SOURCE_PATH "${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}")
|
||||
get_filename_component(JAVA_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin REALPATH BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIRECTORY})
|
||||
add_custom_target(build_java
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY} ${JAVA_SOURCE_PATH}/example/Example.java ${JAVA_SOURCE_PATH}/Client.java ${JAVA_SOURCE_PATH}/Log.java ${JAVA_SOURCE_PATH}/TdApi.java
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY} ${JAVA_SOURCE_PATH}/example/Example.java ${JAVA_SOURCE_PATH}/Client.java ${JAVA_SOURCE_PATH}/TdApi.java
|
||||
COMMENT "Building Java code"
|
||||
DEPENDS td_generate_java_api
|
||||
)
|
||||
|
@ -39,6 +39,21 @@ public final class Client {
|
||||
void onException(Throwable e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for handler of messages that are added to the internal TDLib log.
|
||||
*/
|
||||
public interface LogMessageHandler {
|
||||
/**
|
||||
* Callback called on messages that are added to the internal TDLib log.
|
||||
*
|
||||
* @param verbosityLevel Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* @param message The message added to the internal TDLib log.
|
||||
*/
|
||||
void onLogMessage(int verbosityLevel, String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the TDLib.
|
||||
*
|
||||
@ -105,6 +120,17 @@ public final class Client {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the handler for messages that are added to the internal TDLib log.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
*
|
||||
* @param maxVerbosityLevel The maximum verbosity level of messages for which the callback will be called.
|
||||
* @param logMessageHandler Handler for messages that are added to the internal TDLib log. Pass null to remove the handler.
|
||||
*/
|
||||
public static void setLogMessageHandler(int maxVerbosityLevel, Client.LogMessageHandler logMessageHandler) {
|
||||
nativeClientSetLogMessageHandler(maxVerbosityLevel, logMessageHandler);
|
||||
}
|
||||
|
||||
private static class ResponseReceiver implements Runnable {
|
||||
public boolean isRun = false;
|
||||
|
||||
@ -203,4 +229,6 @@ public final class Client {
|
||||
private static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
|
||||
|
||||
private static native TdApi.Object nativeClientExecute(TdApi.Function function);
|
||||
|
||||
private static native void nativeClientSetLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler);
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
package org.drinkless.tdlib;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging.
|
||||
* Use TdApi.*Log* methods instead.
|
||||
*/
|
||||
public final class Log {
|
||||
/**
|
||||
* Changes TDLib log verbosity.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative.
|
||||
* Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE,
|
||||
* value 2 corresponds to java.util.logging.Level.WARNING,
|
||||
* value 3 corresponds to java.util.logging.Level.INFO,
|
||||
* value 4 corresponds to java.util.logging.Level.FINE,
|
||||
* value 5 corresponds to java.util.logging.Level.FINER,
|
||||
* value greater than 5 can be used to enable even more logging.
|
||||
* Default value of the log verbosity level is 5.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setVerbosityLevel(int verbosityLevel);
|
||||
|
||||
/**
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err.
|
||||
* Use this method to write the log to a file instead.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to
|
||||
* switch back to logging to the System.err.
|
||||
* @return whether opening the log file succeeded.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native boolean setFilePath(String filePath);
|
||||
|
||||
/**
|
||||
* Changes the maximum size of TDLib log file.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written
|
||||
* before the file will be auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setMaxFileSize(long maxFileSize);
|
||||
|
||||
/**
|
||||
* This function is called from the JNI when a fatal error happens to provide a better error message.
|
||||
* The function does not return.
|
||||
*
|
||||
* @param errorMessage Error message.
|
||||
*/
|
||||
private static void onFatalError(String errorMessage) {
|
||||
class ThrowError implements Runnable {
|
||||
private ThrowError(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
throw new RuntimeException("TDLib fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
private final String errorMessage;
|
||||
}
|
||||
|
||||
new Thread(new ThrowError(errorMessage), "TDLib fatal error thread").start();
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000); // milliseconds
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,14 +9,15 @@ package org.drinkless.tdlib.example;
|
||||
import org.drinkless.tdlib.Client;
|
||||
import org.drinkless.tdlib.TdApi;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -298,7 +299,10 @@ public final class Example {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// disable TDLib log
|
||||
// set log message handler to handle only fatal errors (0) and plain log messages (-1)
|
||||
Client.setLogMessageHandler(0, new LogMessageHandler());
|
||||
|
||||
// disable TDLib log and redirect fatal errors and plain log messages to a file
|
||||
Client.execute(new TdApi.SetLogVerbosityLevel(0));
|
||||
if (Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))) instanceof TdApi.Error) {
|
||||
throw new IOError(new IOException("Write access to the current directory is required"));
|
||||
@ -598,4 +602,85 @@ public final class Example {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class LogMessageHandler implements Client.LogMessageHandler {
|
||||
@Override
|
||||
public void onLogMessage(int verbosityLevel, String message) {
|
||||
if (verbosityLevel == 0) {
|
||||
onFatalError(message);
|
||||
return;
|
||||
}
|
||||
System.err.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void onFatalError(String errorMessage) {
|
||||
final class ThrowError implements Runnable {
|
||||
private final String errorMessage;
|
||||
private final AtomicLong errorThrowTime;
|
||||
|
||||
private ThrowError(String errorMessage, AtomicLong errorThrowTime) {
|
||||
this.errorMessage = errorMessage;
|
||||
this.errorThrowTime = errorThrowTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (isDatabaseBrokenError(errorMessage) || isDiskFullError(errorMessage) || isDiskError(errorMessage)) {
|
||||
processExternalError();
|
||||
return;
|
||||
}
|
||||
|
||||
errorThrowTime.set(System.currentTimeMillis());
|
||||
throw new ClientError("TDLib fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
private void processExternalError() {
|
||||
errorThrowTime.set(System.currentTimeMillis());
|
||||
throw new ExternalClientError("Fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
final class ClientError extends Error {
|
||||
private ClientError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
final class ExternalClientError extends Error {
|
||||
public ExternalClientError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDatabaseBrokenError(String message) {
|
||||
return message.contains("Wrong key or database is corrupted") ||
|
||||
message.contains("SQL logic error or missing database") ||
|
||||
message.contains("database disk image is malformed") ||
|
||||
message.contains("file is encrypted or is not a database") ||
|
||||
message.contains("unsupported file format") ||
|
||||
message.contains("Database was corrupted and deleted during execution and can't be recreated");
|
||||
}
|
||||
|
||||
private boolean isDiskFullError(String message) {
|
||||
return message.contains("PosixError : No space left on device") ||
|
||||
message.contains("database or disk is full");
|
||||
}
|
||||
|
||||
private boolean isDiskError(String message) {
|
||||
return message.contains("I/O error") || message.contains("Structure needs cleaning");
|
||||
}
|
||||
}
|
||||
|
||||
final AtomicLong errorThrowTime = new AtomicLong(Long.MAX_VALUE);
|
||||
new Thread(new ThrowError(errorMessage, errorThrowTime), "TDLib fatal error thread").start();
|
||||
|
||||
// wait at least 10 seconds after the error is thrown
|
||||
while (errorThrowTime.get() >= System.currentTimeMillis() - 10000) {
|
||||
try {
|
||||
Thread.sleep(1000 /* milliseconds */);
|
||||
} catch (InterruptedException ignore) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <td/telegram/Client.h>
|
||||
#include <td/telegram/Log.h>
|
||||
#include <td/telegram/td_api.h>
|
||||
|
||||
#include <td/tl/tl_jni_object.h>
|
||||
@ -77,18 +76,6 @@ static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject fun
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Log_setVerbosityLevel(JNIEnv *env, jclass clazz, jint new_log_verbosity_level) {
|
||||
td::Log::set_verbosity_level(static_cast<int>(new_log_verbosity_level));
|
||||
}
|
||||
|
||||
static jboolean Log_setFilePath(JNIEnv *env, jclass clazz, jstring file_path) {
|
||||
return td::Log::set_file_path(td::jni::from_jstring(env, file_path)) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static void Log_setMaxFileSize(JNIEnv *env, jclass clazz, jlong max_file_size) {
|
||||
td::Log::set_max_file_size(max_file_size);
|
||||
}
|
||||
|
||||
static jstring Object_toString(JNIEnv *env, jobject object) {
|
||||
return td::jni::to_jstring(env, to_string(td::td_api::Object::fetch(env, object)));
|
||||
}
|
||||
@ -99,23 +86,52 @@ static jstring Function_toString(JNIEnv *env, jobject object) {
|
||||
|
||||
static constexpr jint JAVA_VERSION = JNI_VERSION_1_6;
|
||||
static JavaVM *java_vm;
|
||||
static jclass log_class;
|
||||
static jobject log_message_handler;
|
||||
|
||||
static void on_log_message(int verbosity_level, const char *error_message) {
|
||||
if (verbosity_level != 0) {
|
||||
return;
|
||||
}
|
||||
static void on_log_message(int verbosity_level, const char *log_message) {
|
||||
auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION);
|
||||
if (env == nullptr) {
|
||||
return;
|
||||
}
|
||||
jmethodID on_fatal_error_method = env->GetStaticMethodID(log_class, "onFatalError", "(Ljava/lang/String;)V");
|
||||
if (on_fatal_error_method) {
|
||||
jstring error_str = td::jni::to_jstring(env.get(), error_message);
|
||||
env->CallStaticVoidMethod(log_class, on_fatal_error_method, error_str);
|
||||
if (error_str) {
|
||||
env->DeleteLocalRef(error_str);
|
||||
|
||||
jobject handler = env->NewLocalRef(log_message_handler);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
jclass handler_class = env->GetObjectClass(handler);
|
||||
if (handler_class) {
|
||||
jmethodID on_log_message_method = env->GetMethodID(handler_class, "onLogMessage", "(ILjava/lang/String;)V");
|
||||
if (on_log_message_method) {
|
||||
jstring log_message_str = td::jni::to_jstring(env.get(), log_message);
|
||||
if (log_message_str) {
|
||||
env->CallVoidMethod(handler, on_log_message_method, static_cast<jint>(verbosity_level), log_message_str);
|
||||
env->DeleteLocalRef((jobject)log_message_str);
|
||||
}
|
||||
}
|
||||
env->DeleteLocalRef((jobject)handler_class);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(handler);
|
||||
}
|
||||
|
||||
static void Client_nativeClientSetLogMessageHandler(JNIEnv *env, jclass clazz, jint max_verbosity_level,
|
||||
jobject new_log_message_handler) {
|
||||
if (log_message_handler) {
|
||||
td::ClientManager::set_log_message_callback(0, nullptr);
|
||||
jobject old_log_message_handler = log_message_handler;
|
||||
log_message_handler = jobject();
|
||||
env->DeleteGlobalRef(old_log_message_handler);
|
||||
}
|
||||
|
||||
if (new_log_message_handler) {
|
||||
log_message_handler = env->NewGlobalRef(new_log_message_handler);
|
||||
if (!log_message_handler) {
|
||||
// out of memory
|
||||
return;
|
||||
}
|
||||
|
||||
td::ClientManager::set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +149,6 @@ static jint register_native(JavaVM *vm) {
|
||||
};
|
||||
|
||||
auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client");
|
||||
log_class = td::jni::get_jclass(env, PACKAGE_NAME "/Log");
|
||||
auto object_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Object");
|
||||
auto function_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Function");
|
||||
|
||||
@ -143,10 +158,8 @@ static jint register_native(JavaVM *vm) {
|
||||
register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend);
|
||||
register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive);
|
||||
register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute);
|
||||
|
||||
register_method(log_class, "setVerbosityLevel", "(I)V", Log_setVerbosityLevel);
|
||||
register_method(log_class, "setFilePath", "(Ljava/lang/String;)Z", Log_setFilePath);
|
||||
register_method(log_class, "setMaxFileSize", "(J)V", Log_setMaxFileSize);
|
||||
register_method(client_class, "nativeClientSetLogMessageHandler", "(IL" PACKAGE_NAME "/Client$LogMessageHandler;)V",
|
||||
Client_nativeClientSetLogMessageHandler);
|
||||
|
||||
register_method(object_class, "toString", "()Ljava/lang/String;", Object_toString);
|
||||
|
||||
@ -157,7 +170,6 @@ static jint register_native(JavaVM *vm) {
|
||||
td::jni::init_vars(env, PACKAGE_NAME);
|
||||
td::td_api::Object::init_jni_vars(env, PACKAGE_NAME);
|
||||
td::td_api::Function::init_jni_vars(env, PACKAGE_NAME);
|
||||
td::ClientManager::set_log_message_callback(0, on_log_message);
|
||||
|
||||
return JAVA_VERSION;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
$commit = git rev-parse HEAD
|
||||
git diff-index --quiet HEAD
|
||||
$dirty = $LASTEXITCODE
|
||||
$commit = try { git rev-parse HEAD } catch { "unknown" }
|
||||
try { git diff-index --quiet HEAD } catch {}
|
||||
$dirty = if ($LASTEXITCODE) { "true" } else { "false" }
|
||||
echo "#pragma once`r`n#define GIT_COMMIT `"$commit`"`r`n#define GIT_DIRTY $dirty" | out-file -encoding ASCII auto/git_info.h.new
|
||||
if (-not (Test-Path .\auto\git_info.h) -or (Compare-Object $(Get-Content .\auto\git_info.h.new) $(Get-Content .\auto\git_info.h))) {
|
||||
mv -Force auto/git_info.h.new auto/git_info.h
|
||||
|
@ -1,8 +1,14 @@
|
||||
#!/bin/sh
|
||||
cd $(dirname $0)
|
||||
commit=$(git rev-parse HEAD)
|
||||
git diff-index --quiet HEAD
|
||||
dirty=$?
|
||||
commit="$(git rev-parse HEAD 2> /dev/null)"
|
||||
commit="${commit:-unknown}"
|
||||
git diff-index --quiet HEAD 2> /dev/null
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
dirty="true"
|
||||
else
|
||||
dirty="false"
|
||||
fi
|
||||
printf "#pragma once\n#define GIT_COMMIT \"$commit\"\n#define GIT_DIRTY $dirty\n" > auto/git_info.h.new
|
||||
if cmp -s auto/git_info.h.new auto/git_info.h 2>&1 > /dev/null
|
||||
then
|
||||
|
@ -6217,7 +6217,7 @@ setUserPrivacySettingRules setting:UserPrivacySetting rules:userPrivacySettingRu
|
||||
getUserPrivacySettingRules setting:UserPrivacySetting = UserPrivacySettingRules;
|
||||
|
||||
|
||||
//@description Returns the value of an option by its name. (Check the list of available options on https://core.telegram.org/tdlib/options.) Can be called before authorization
|
||||
//@description Returns the value of an option by its name. (Check the list of available options on https://core.telegram.org/tdlib/options.) Can be called before authorization. Can be called synchronously for options "version" and "commit_hash"
|
||||
//@name The name of the option
|
||||
getOption name:string = OptionValue;
|
||||
|
||||
|
@ -26,6 +26,7 @@ inputStickerSetThumbLegacy#dbaeae9 stickerset:InputStickerSet volume_id:long loc
|
||||
|
||||
test.useError = Error;
|
||||
test.useConfigSimple = help.ConfigSimple;
|
||||
test.parseInputAppEvent = InputAppEvent;
|
||||
|
||||
---types---
|
||||
|
||||
|
@ -140,6 +140,10 @@ AnimationsManager::AnimationsManager(Td *td, ActorShared<> parent) : td_(td), pa
|
||||
next_saved_animations_load_time_ = Time::now();
|
||||
}
|
||||
|
||||
AnimationsManager::~AnimationsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), animations_);
|
||||
}
|
||||
|
||||
void AnimationsManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
@ -29,6 +29,11 @@ class Td;
|
||||
class AnimationsManager final : public Actor {
|
||||
public:
|
||||
AnimationsManager(Td *td, ActorShared<> parent);
|
||||
AnimationsManager(const AnimationsManager &) = delete;
|
||||
AnimationsManager &operator=(const AnimationsManager &) = delete;
|
||||
AnimationsManager(AnimationsManager &&) = delete;
|
||||
AnimationsManager &operator=(AnimationsManager &&) = delete;
|
||||
~AnimationsManager() final;
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -7,11 +7,18 @@
|
||||
#include "td/telegram/Application.h"
|
||||
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/logevent/LogEvent.h"
|
||||
#include "td/telegram/logevent/LogEventHelper.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
|
||||
#include "td/db/binlog/BinlogEvent.h"
|
||||
#include "td/db/binlog/BinlogHelper.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -48,11 +55,9 @@ class SaveAppLogQuery final : public Td::ResultHandler {
|
||||
explicit SaveAppLogQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data) {
|
||||
CHECK(data != nullptr);
|
||||
void send(telegram_api::object_ptr<telegram_api::inputAppEvent> &&input_app_event) {
|
||||
vector<telegram_api::object_ptr<telegram_api::inputAppEvent>> input_app_events;
|
||||
input_app_events.push_back(
|
||||
make_tl_object<telegram_api::inputAppEvent>(G()->server_time_cached(), type, dialog_id.get(), std::move(data)));
|
||||
input_app_events.push_back(std::move(input_app_event));
|
||||
send_query(G()->net_query_creator().create_unauth(telegram_api::help_saveAppLog(std::move(input_app_events))));
|
||||
}
|
||||
|
||||
@ -76,9 +81,55 @@ void get_invite_text(Td *td, Promise<string> &&promise) {
|
||||
td->create_handler<GetInviteTextQuery>(std::move(promise))->send();
|
||||
}
|
||||
|
||||
class SaveAppLogLogEvent {
|
||||
public:
|
||||
const telegram_api::inputAppEvent *input_app_event_in_ = nullptr;
|
||||
telegram_api::object_ptr<telegram_api::inputAppEvent> input_app_event_out_;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
input_app_event_in_->store(storer);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
auto buffer = parser.template fetch_string_raw<BufferSlice>(parser.get_left_len());
|
||||
TlBufferParser buffer_parser{&buffer};
|
||||
input_app_event_out_ = telegram_api::make_object<telegram_api::inputAppEvent>(buffer_parser);
|
||||
}
|
||||
};
|
||||
|
||||
static void save_app_log_impl(Td *td, telegram_api::object_ptr<telegram_api::inputAppEvent> input_app_event,
|
||||
uint64 log_event_id, Promise<Unit> &&promise) {
|
||||
if (log_event_id == 0) {
|
||||
SaveAppLogLogEvent log_event;
|
||||
log_event.input_app_event_in_ = input_app_event.get();
|
||||
log_event_id =
|
||||
binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::SaveAppLog, get_log_event_storer(log_event));
|
||||
}
|
||||
|
||||
td->create_handler<SaveAppLogQuery>(get_erase_log_event_promise(log_event_id, std::move(promise)))
|
||||
->send(std::move(input_app_event));
|
||||
}
|
||||
|
||||
void save_app_log(Td *td, const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data,
|
||||
Promise<Unit> &&promise) {
|
||||
td->create_handler<SaveAppLogQuery>(std::move(promise))->send(type, dialog_id, std::move(data));
|
||||
CHECK(data != nullptr);
|
||||
auto input_app_event = telegram_api::make_object<telegram_api::inputAppEvent>(G()->server_time_cached(), type,
|
||||
dialog_id.get(), std::move(data));
|
||||
save_app_log_impl(td, std::move(input_app_event), 0, std::move(promise));
|
||||
}
|
||||
|
||||
void on_save_app_log_binlog_event(Td *td, BinlogEvent &&event) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
CHECK(event.id_ != 0);
|
||||
CHECK(event.type_ == LogEvent::HandlerType::SaveAppLog);
|
||||
SaveAppLogLogEvent log_event;
|
||||
log_event_parse(log_event, event.data_).ensure();
|
||||
|
||||
save_app_log_impl(td, std::move(log_event.input_app_event_out_), event.id_, Promise<Unit>());
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
struct BinlogEvent;
|
||||
|
||||
class Td;
|
||||
|
||||
void get_invite_text(Td *td, Promise<string> &&promise);
|
||||
@ -21,4 +23,6 @@ void get_invite_text(Td *td, Promise<string> &&promise);
|
||||
void save_app_log(Td *td, const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data,
|
||||
Promise<Unit> &&promise);
|
||||
|
||||
void on_save_app_log_binlog_event(Td *td, BinlogEvent &&event);
|
||||
|
||||
} // namespace td
|
||||
|
@ -24,6 +24,10 @@ namespace td {
|
||||
AudiosManager::AudiosManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
AudiosManager::~AudiosManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), audios_);
|
||||
}
|
||||
|
||||
int32 AudiosManager::get_audio_duration(FileId file_id) const {
|
||||
auto it = audios_.find(file_id);
|
||||
if (it == audios_.end()) {
|
||||
|
@ -23,6 +23,11 @@ class Td;
|
||||
class AudiosManager {
|
||||
public:
|
||||
explicit AudiosManager(Td *td);
|
||||
AudiosManager(const AudiosManager &) = delete;
|
||||
AudiosManager &operator=(const AudiosManager &) = delete;
|
||||
AudiosManager(AudiosManager &&) = delete;
|
||||
AudiosManager &operator=(AudiosManager &&) = delete;
|
||||
~AudiosManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -249,8 +249,8 @@ class MultiTd final : public Actor {
|
||||
auto old_context = set_context(context);
|
||||
auto old_tag = set_tag(to_string(td_id));
|
||||
td = create_actor<Td>("Td", std::move(callback), options_);
|
||||
set_context(old_context);
|
||||
set_tag(old_tag);
|
||||
set_context(std::move(old_context));
|
||||
set_tag(std::move(old_tag));
|
||||
}
|
||||
|
||||
void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id,
|
||||
|
@ -126,7 +126,7 @@ class ClientManager final {
|
||||
/**
|
||||
* A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
*
|
||||
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024).
|
||||
* \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* \param message Null-terminated UTF-8-encoded string with the message added to the log.
|
||||
|
@ -25,10 +25,10 @@ using namespace CxCli;
|
||||
/// <summary>
|
||||
/// A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
/// </summary>
|
||||
/// <param name="verbosityLevel">Log verbosity level with which the message was added (-1 - 1024).
|
||||
/// <param name="verbosityLevel">Log verbosity level with which the message was added from -1 up to 1024.
|
||||
/// If 0, then TDLib will crash as soon as the callback returns.
|
||||
/// None of the TDLib methods can be called from the callback.</param>
|
||||
/// <param name="message">Null-terminated string with the message added to the log.</param>
|
||||
/// <param name="message">The message added to the log.</param>
|
||||
public delegate void LogMessageCallback(int verbosityLevel, String^ message);
|
||||
#endif
|
||||
|
||||
|
@ -3398,7 +3398,45 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent
|
||||
channel_participant_cache_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
ContactsManager::~ContactsManager() = default;
|
||||
ContactsManager::~ContactsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), users_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), user_photos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), pending_user_photos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), user_profile_photo_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), my_photo_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chats_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chat_full_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), min_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channels_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), invalidated_channels_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_full_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), secret_chats_with_user_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), invite_link_infos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_access_by_invite_link_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_user_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_chat_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_channel_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_administrators_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), cached_channel_participants_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), resolved_phone_numbers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_participants_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), all_imported_contacts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), linked_channel_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), restricted_user_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), restricted_channel_ids_);
|
||||
}
|
||||
|
||||
void ContactsManager::tear_down() {
|
||||
parent_.reset();
|
||||
@ -5414,7 +5452,7 @@ std::pair<vector<UserId>, vector<int32>> ContactsManager::import_contacts(const
|
||||
|
||||
do {
|
||||
random_id = Random::secure_int64();
|
||||
} while (random_id == 0 || imported_contacts_.count(random_id) > 0);
|
||||
} while (random_id == 0 || random_id == 1 || imported_contacts_.count(random_id) > 0);
|
||||
imported_contacts_[random_id]; // reserve place for result
|
||||
|
||||
do_import_contacts(contacts, random_id, std::move(promise));
|
||||
@ -5759,7 +5797,7 @@ void ContactsManager::on_clear_imported_contacts(vector<Contact> &&contacts, vec
|
||||
imported_contacts_unique_id_ = std::move(contacts_unique_id);
|
||||
imported_contacts_pos_ = std::move(to_add.first);
|
||||
|
||||
do_import_contacts(std::move(to_add.second), 0, std::move(promise));
|
||||
do_import_contacts(std::move(to_add.second), 1, std::move(promise));
|
||||
}
|
||||
|
||||
void ContactsManager::clear_imported_contacts(Promise<Unit> &&promise) {
|
||||
@ -8250,7 +8288,7 @@ void ContactsManager::on_import_contacts_finished(int64 random_id, vector<UserId
|
||||
vector<int32> unimported_contact_invites) {
|
||||
LOG(INFO) << "Contacts import with random_id " << random_id
|
||||
<< " has finished: " << format::as_array(imported_contact_user_ids);
|
||||
if (random_id == 0) {
|
||||
if (random_id == 1) {
|
||||
// import from change_imported_contacts
|
||||
all_imported_contacts_ = std::move(next_all_imported_contacts_);
|
||||
next_all_imported_contacts_.clear();
|
||||
@ -16735,7 +16773,19 @@ tl_object_ptr<td_api::userFullInfo> ContactsManager::get_user_full_info_object(U
|
||||
} else {
|
||||
FormattedText bio;
|
||||
bio.text = user_full->about;
|
||||
bio.entities = find_entities(bio.text, true, true, !is_user_premium(user_id));
|
||||
bio.entities = find_entities(bio.text, true, true);
|
||||
if (!is_user_premium(user_id)) {
|
||||
td::remove_if(bio.entities, [&](const MessageEntity &entity) {
|
||||
if (entity.type == MessageEntity::Type::EmailAddress) {
|
||||
return true;
|
||||
}
|
||||
if (entity.type == MessageEntity::Type::Url &&
|
||||
!LinkManager::is_internal_link(utf8_utf16_substr(Slice(bio.text), entity.offset, entity.length))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
bio_object = get_formatted_text_object(bio, true, 0);
|
||||
}
|
||||
return make_tl_object<td_api::userFullInfo>(
|
||||
|
@ -48,6 +48,10 @@ namespace td {
|
||||
DocumentsManager::DocumentsManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
DocumentsManager::~DocumentsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), documents_);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId file_id,
|
||||
PhotoFormat thumbnail_format) const {
|
||||
if (!file_id.is_valid()) {
|
||||
|
@ -31,6 +31,11 @@ class Td;
|
||||
class DocumentsManager {
|
||||
public:
|
||||
explicit DocumentsManager(Td *td);
|
||||
DocumentsManager(const DocumentsManager &) = delete;
|
||||
DocumentsManager &operator=(const DocumentsManager &) = delete;
|
||||
DocumentsManager(DocumentsManager &&) = delete;
|
||||
DocumentsManager &operator=(DocumentsManager &&) = delete;
|
||||
~DocumentsManager();
|
||||
|
||||
class RemoteDocument {
|
||||
public:
|
||||
|
@ -352,7 +352,7 @@ class DownloadManagerImpl final : public DownloadManager {
|
||||
FileId file_id;
|
||||
FileId internal_file_id;
|
||||
FileSourceId file_source_id;
|
||||
int8 priority;
|
||||
int8 priority{};
|
||||
bool is_paused{};
|
||||
bool is_counted{};
|
||||
mutable bool is_registered{};
|
||||
@ -567,8 +567,11 @@ class DownloadManagerImpl final : public DownloadManager {
|
||||
<< file_info->size << '/' << file_info->expected_size
|
||||
<< " with downloaded_size = " << file_info->downloaded_size
|
||||
<< " and is_paused = " << file_info->is_paused;
|
||||
auto it = files_.emplace(download_id, std::move(file_info)).first;
|
||||
auto res = files_.emplace(download_id, std::move(file_info));
|
||||
auto it = res.first;
|
||||
bool was_completed = is_completed(*it->second);
|
||||
LOG_CHECK(!it->second->is_registered)
|
||||
<< res.second << ' ' << download_id << ' ' << max_download_id_ << ' ' << it->second->need_save_to_database;
|
||||
register_file_info(*it->second); // must be called before start_file, which can call update_file_download_state
|
||||
if (is_completed(*it->second)) {
|
||||
bool is_inserted = completed_download_ids_.insert(it->second->download_id).second;
|
||||
|
@ -69,7 +69,8 @@ FileView DownloadManagerCallback::get_file_view(FileId file_id) {
|
||||
}
|
||||
|
||||
FileView DownloadManagerCallback::get_sync_file_view(FileId file_id) {
|
||||
return td_->file_manager_->get_sync_file_view(file_id);
|
||||
td_->file_manager_->check_local_location(file_id);
|
||||
return get_file_view(file_id);
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::fileDownload> DownloadManagerCallback::get_file_download_object(
|
||||
|
@ -33,6 +33,10 @@ namespace td {
|
||||
|
||||
int VERBOSITY_NAME(file_references) = VERBOSITY_NAME(INFO);
|
||||
|
||||
FileReferenceManager::~FileReferenceManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_sources_);
|
||||
}
|
||||
|
||||
bool FileReferenceManager::is_file_reference_error(const Status &error) {
|
||||
return error.is_error() && error.code() == 400 && begins_with(error.message(), "FILE_REFERENCE_");
|
||||
}
|
||||
|
@ -35,6 +35,13 @@ extern int VERBOSITY_NAME(file_references);
|
||||
|
||||
class FileReferenceManager final : public Actor {
|
||||
public:
|
||||
FileReferenceManager() = default;
|
||||
FileReferenceManager(const FileReferenceManager &) = delete;
|
||||
FileReferenceManager &operator=(const FileReferenceManager &) = delete;
|
||||
FileReferenceManager(FileReferenceManager &&) = delete;
|
||||
FileReferenceManager &operator=(FileReferenceManager &&) = delete;
|
||||
~FileReferenceManager() final;
|
||||
|
||||
static bool is_file_reference_error(const Status &error);
|
||||
static size_t get_file_reference_error_pos(const Status &error);
|
||||
|
||||
|
@ -4,17 +4,12 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/GitInfo.h"
|
||||
|
||||
#include "auto/git_info.h"
|
||||
#include "td/telegram/GitCommitHash.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
CSlice GitInfo::commit() {
|
||||
return GIT_COMMIT;
|
||||
}
|
||||
bool GitInfo::is_dirty() {
|
||||
return GIT_DIRTY;
|
||||
const char *get_git_commit_hash() {
|
||||
return "@TD_GIT_COMMIT_HASH@";
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -6,14 +6,8 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class GitInfo {
|
||||
public:
|
||||
static CSlice commit();
|
||||
static bool is_dirty();
|
||||
};
|
||||
const char *get_git_commit_hash();
|
||||
|
||||
} // namespace td
|
@ -924,6 +924,11 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LinkManager::is_internal_link(Slice link) {
|
||||
auto info = get_link_info(link);
|
||||
return info.is_internal_;
|
||||
}
|
||||
|
||||
unique_ptr<LinkManager::InternalLink> LinkManager::parse_internal_link(Slice link, bool is_trusted) {
|
||||
auto info = get_link_info(link);
|
||||
if (!info.is_internal_) {
|
||||
|
@ -52,6 +52,9 @@ class LinkManager final : public Actor {
|
||||
// same as check_link, but returns an empty string instead of an error
|
||||
static string get_checked_link(Slice link, bool http_only = false, bool https_only = false);
|
||||
|
||||
// returns whether a link is an internal link, supported or not
|
||||
static bool is_internal_link(Slice link);
|
||||
|
||||
// checks whether the link is a supported tg or t.me link and parses it
|
||||
static unique_ptr<InternalLink> parse_internal_link(Slice link, bool is_trusted = false);
|
||||
|
||||
|
@ -453,7 +453,7 @@ class MessageChatSetTtl final : public MessageContent {
|
||||
|
||||
class MessageUnsupported final : public MessageContent {
|
||||
public:
|
||||
static constexpr int32 CURRENT_VERSION = 11;
|
||||
static constexpr int32 CURRENT_VERSION = 12;
|
||||
int32 version = CURRENT_VERSION;
|
||||
|
||||
MessageUnsupported() = default;
|
||||
@ -3466,8 +3466,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
||||
old_->telegram_payment_charge_id != new_->telegram_payment_charge_id ||
|
||||
old_->provider_payment_charge_id != new_->provider_payment_charge_id ||
|
||||
((old_->order_info != nullptr || new_->order_info != nullptr) &&
|
||||
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info ||
|
||||
old_->is_recurring != new_->is_recurring || old_->is_first_recurring != new_->is_first_recurring))) {
|
||||
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info)) ||
|
||||
old_->is_recurring != new_->is_recurring || old_->is_first_recurring != new_->is_first_recurring) {
|
||||
need_update = true;
|
||||
}
|
||||
break;
|
||||
|
@ -1636,7 +1636,7 @@ static void fix_entity_offsets(Slice text, vector<MessageEntity> &entities) {
|
||||
}
|
||||
}
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps, bool skip_urls) {
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) {
|
||||
vector<MessageEntity> entities;
|
||||
|
||||
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
|
||||
@ -1655,7 +1655,6 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool ski
|
||||
add_entities(MessageEntity::Type::Cashtag, find_cashtags);
|
||||
// TODO find_phone_numbers
|
||||
add_entities(MessageEntity::Type::BankCardNumber, find_bank_card_numbers);
|
||||
if (!skip_urls) {
|
||||
add_entities(MessageEntity::Type::Url, find_tg_urls);
|
||||
auto urls = find_urls(text);
|
||||
for (auto &url : urls) {
|
||||
@ -1664,7 +1663,6 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool ski
|
||||
auto length = narrow_cast<int32>(url.first.size());
|
||||
entities.emplace_back(type, offset, length);
|
||||
}
|
||||
}
|
||||
if (!skip_media_timestamps) {
|
||||
auto media_timestamps = find_media_timestamps(text);
|
||||
for (auto &entity : media_timestamps) {
|
||||
|
@ -143,8 +143,7 @@ vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<
|
||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
|
||||
int32 max_media_timestamp);
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps,
|
||||
bool skip_urls = false);
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
|
||||
|
||||
vector<Slice> find_mentions(Slice str);
|
||||
vector<Slice> find_bot_commands(Slice str);
|
||||
|
@ -5755,12 +5755,6 @@ void MessagesManager::save_calls_db_state() {
|
||||
G()->td_db()->get_sqlite_pmc()->set("calls_db_state", log_event_store(calls_db_state_).as_slice().str(), Auto());
|
||||
}
|
||||
|
||||
MessagesManager::Dialog::~Dialog() {
|
||||
if (!G()->close_flag()) {
|
||||
LOG(ERROR) << "Destroy " << dialog_id;
|
||||
}
|
||||
}
|
||||
|
||||
MessagesManager::MessagesManager(Td *td, ActorShared<> parent)
|
||||
: recently_found_dialogs_{td, "recently_found", MAX_RECENT_DIALOGS}
|
||||
, recently_opened_dialogs_{td, "recently_opened", MAX_RECENT_DIALOGS}
|
||||
@ -5815,7 +5809,36 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent)
|
||||
update_viewed_messages_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
MessagesManager::~MessagesManager() = default;
|
||||
MessagesManager::~MessagesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), ttl_nodes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), ttl_heap_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), being_sent_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), update_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), update_scheduled_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), last_clear_history_message_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), postponed_chat_read_inbox_updates_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_public_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_on_server_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_common_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_embedding_codes_[0]);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_embedding_codes_[1]);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), replied_by_media_timestamp_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), notification_group_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), active_get_channel_differencies_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), get_channel_difference_to_log_event_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_get_difference_retry_timeouts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), is_channel_difference_finished_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), resolved_usernames_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), inaccessible_resolved_usernames_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_bot_command_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), full_message_id_to_file_source_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), last_outgoing_forwarded_message_date_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_viewed_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_online_member_counts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), previous_repaired_read_inbox_max_message_id_);
|
||||
}
|
||||
|
||||
void MessagesManager::on_channel_get_difference_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int) {
|
||||
if (G()->close_flag()) {
|
||||
@ -14887,9 +14910,9 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n
|
||||
|
||||
auto last_new_message = get_message(d, last_new_message_id);
|
||||
if (last_new_message != nullptr) {
|
||||
add_message_to_database(d, last_new_message, "set_dialog_last_new_message_id");
|
||||
set_dialog_first_database_message_id(d, last_new_message_id, "set_dialog_last_new_message_id");
|
||||
set_dialog_last_database_message_id(d, last_new_message_id, "set_dialog_last_new_message_id");
|
||||
add_message_to_database(d, last_new_message, source);
|
||||
set_dialog_first_database_message_id(d, last_new_message_id, source);
|
||||
set_dialog_last_database_message_id(d, last_new_message_id, source);
|
||||
try_restore_dialog_reply_markup(d, last_new_message);
|
||||
}
|
||||
}
|
||||
@ -36296,6 +36319,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
const char *source) {
|
||||
auto dialog_id = d->dialog_id;
|
||||
LOG_CHECK(is_inited_) << dialog_id << ' ' << is_loaded_from_database << ' ' << source;
|
||||
LOG_CHECK(!have_dialog(dialog_id)) << dialog_id << ' ' << is_loaded_from_database << ' ' << source;
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
if (dialog_id == get_my_dialog_id() && d->last_read_inbox_message_id == MessageId::max() &&
|
||||
@ -36438,6 +36462,11 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
|
||||
being_added_new_dialog_id_ = DialogId();
|
||||
|
||||
LOG_CHECK(dialog->messages == nullptr) << dialog->messages->message_id << ' ' << dialog->last_message_id << ' '
|
||||
<< dialog->last_database_message_id << ' '
|
||||
<< dialog->debug_set_dialog_last_database_message_id << ' '
|
||||
<< dialog->messages->debug_source;
|
||||
|
||||
fix_new_dialog(dialog, std::move(last_database_message), last_database_message_id, order, last_clear_history_date,
|
||||
last_clear_history_message_id, default_join_group_call_as_dialog_id, default_send_message_as_dialog_id,
|
||||
need_drop_default_send_message_as_dialog_id, is_loaded_from_database);
|
||||
@ -36797,13 +36826,16 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
|
||||
if (d->messages != nullptr) {
|
||||
LOG_CHECK(d->messages->message_id == last_message_id)
|
||||
<< d->messages->message_id << ' ' << last_message_id << ' ' << d->debug_set_dialog_last_database_message_id
|
||||
<< ' ' << d->messages->debug_source;
|
||||
<< d->messages->message_id << ' ' << last_message_id << ' ' << d->last_message_id << ' '
|
||||
<< d->last_database_message_id << ' ' << d->debug_set_dialog_last_database_message_id << ' '
|
||||
<< d->messages->debug_source;
|
||||
LOG_CHECK(d->messages->left == nullptr)
|
||||
<< d->messages->left->message_id << ' ' << d->messages->message_id << ' ' << last_message_id << ' '
|
||||
<< d->messages->left->message_id << ' ' << d->messages->message_id << ' ' << d->messages->left->message_id
|
||||
<< ' ' << last_message_id << ' ' << d->last_message_id << ' ' << d->last_database_message_id << ' '
|
||||
<< d->debug_set_dialog_last_database_message_id << ' ' << d->messages->debug_source;
|
||||
LOG_CHECK(d->messages->right == nullptr)
|
||||
<< d->messages->right->message_id << ' ' << d->messages->message_id << ' ' << last_message_id << ' '
|
||||
<< d->messages->right->message_id << ' ' << d->messages->message_id << ' ' << d->messages->right->message_id
|
||||
<< ' ' << last_message_id << ' ' << d->last_message_id << ' ' << d->last_database_message_id << ' '
|
||||
<< d->debug_set_dialog_last_database_message_id << ' ' << d->messages->debug_source;
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1433,7 @@ class MessagesManager final : public Actor {
|
||||
Dialog &operator=(const Dialog &) = delete;
|
||||
Dialog(Dialog &&other) = delete;
|
||||
Dialog &operator=(Dialog &&other) = delete;
|
||||
~Dialog();
|
||||
~Dialog() = default;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const;
|
||||
|
@ -984,6 +984,17 @@ void NotificationManager::add_update_notification_group(td_api::object_ptr<td_ap
|
||||
if (update->notification_settings_chat_id_ == 0) {
|
||||
update->notification_settings_chat_id_ = update->chat_id_;
|
||||
}
|
||||
if (!update->added_notifications_.empty() && !update->removed_notification_ids_.empty()) {
|
||||
// just in case
|
||||
td::remove_if(update->added_notifications_, [&](const td_api::object_ptr<td_api::notification> ¬ification) {
|
||||
CHECK(notification != nullptr);
|
||||
if (td::contains(update->removed_notification_ids_, notification->id_)) {
|
||||
LOG(ERROR) << "Have the same notification as added and removed";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
add_update(group_id, std::move(update));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "td/telegram/ConfigShared.h"
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/GitCommitHash.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/JsonValue.h"
|
||||
#include "td/telegram/LanguagePackManager.h"
|
||||
@ -28,6 +29,7 @@
|
||||
#include "td/telegram/telegram_api.h"
|
||||
#include "td/telegram/TopDialogManager.h"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
@ -210,6 +212,15 @@ bool OptionManager::is_internal_option(Slice name) {
|
||||
}
|
||||
}
|
||||
|
||||
const vector<Slice> &OptionManager::get_synchronous_options() {
|
||||
static const vector<Slice> options{"version", "commit_hash"};
|
||||
return options;
|
||||
}
|
||||
|
||||
bool OptionManager::is_synchronous_option(Slice name) {
|
||||
return td::contains(get_synchronous_options(), name);
|
||||
}
|
||||
|
||||
void OptionManager::on_option_updated(const string &name) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
@ -424,13 +435,25 @@ void OptionManager::get_option(const string &name, Promise<td_api::object_ptr<td
|
||||
return promise.set_value(get_unix_time_option_value_object());
|
||||
}
|
||||
break;
|
||||
}
|
||||
wrap_promise().set_value(Unit());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_synchronously(Slice name) {
|
||||
CHECK(!name.empty());
|
||||
switch (name[0]) {
|
||||
case 'c':
|
||||
if (name == "commit_hash") {
|
||||
return td_api::make_object<td_api::optionValueString>(get_git_commit_hash());
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
if (name == "version") {
|
||||
return promise.set_value(td_api::make_object<td_api::optionValueString>(Td::TDLIB_VERSION));
|
||||
return td_api::make_object<td_api::optionValueString>("1.8.4");
|
||||
}
|
||||
break;
|
||||
}
|
||||
wrap_promise().set_value(Unit());
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void OptionManager::set_option(const string &name, td_api::object_ptr<td_api::OptionValue> &&value,
|
||||
@ -792,9 +815,15 @@ td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_value_object(S
|
||||
return td_api::make_object<td_api::optionValueString>(value.str());
|
||||
}
|
||||
|
||||
void OptionManager::get_common_state(vector<td_api::object_ptr<td_api::Update>> &updates) {
|
||||
for (auto option_name : get_synchronous_options()) {
|
||||
updates.push_back(
|
||||
td_api::make_object<td_api::updateOption>(option_name.str(), get_option_synchronously(option_name)));
|
||||
}
|
||||
}
|
||||
|
||||
void OptionManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"version", td_api::make_object<td_api::optionValueString>(Td::TDLIB_VERSION)));
|
||||
get_common_state(updates);
|
||||
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"online", td_api::make_object<td_api::optionValueBoolean>(td_->is_online())));
|
||||
|
@ -38,6 +38,12 @@ class OptionManager final : public Actor {
|
||||
|
||||
static void clear_options();
|
||||
|
||||
static bool is_synchronous_option(Slice name);
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_option_synchronously(Slice name);
|
||||
|
||||
static void get_common_state(vector<td_api::object_ptr<td_api::Update>> &updates);
|
||||
|
||||
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
|
||||
|
||||
private:
|
||||
@ -45,6 +51,8 @@ class OptionManager final : public Actor {
|
||||
|
||||
static bool is_internal_option(Slice name);
|
||||
|
||||
static const vector<Slice> &get_synchronous_options();
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_unix_time_option_value_object();
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_option_value_object(Slice value);
|
||||
|
@ -266,7 +266,13 @@ void PollManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
||||
PollManager::~PollManager() = default;
|
||||
PollManager::~PollManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), polls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), server_poll_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), other_poll_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), poll_voters_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_polls_);
|
||||
}
|
||||
|
||||
void PollManager::on_update_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) {
|
||||
if (G()->close_flag()) {
|
||||
|
@ -30,7 +30,7 @@ class SecretChatsManager final : public Actor {
|
||||
public:
|
||||
explicit SecretChatsManager(ActorShared<> parent);
|
||||
|
||||
// proxy query to corrensponding SecretChatActor
|
||||
// proxy query to corresponding SecretChatActor
|
||||
void on_update_chat(tl_object_ptr<telegram_api::updateEncryption> update);
|
||||
void on_new_message(tl_object_ptr<telegram_api::EncryptedMessage> &&message_ptr, Promise<Unit> &&promise);
|
||||
|
||||
|
@ -10,6 +10,23 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type) {
|
||||
CHECK(type != nullptr);
|
||||
switch (type->get_id()) {
|
||||
case td_api::stickerTypeStatic::ID:
|
||||
return StickerFormat::Webp;
|
||||
case td_api::stickerTypeAnimated::ID:
|
||||
return StickerFormat::Tgs;
|
||||
case td_api::stickerTypeVideo::ID:
|
||||
return StickerFormat::Webm;
|
||||
case td_api::stickerTypeMask::ID:
|
||||
return StickerFormat::Webp;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return StickerFormat::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
StickerFormat get_sticker_format_by_mime_type(Slice mime_type) {
|
||||
if (mime_type == "application/x-tgsticker") {
|
||||
return StickerFormat::Tgs;
|
||||
|
@ -18,6 +18,8 @@ namespace td {
|
||||
// update store_sticker/store_sticker_set when this type changes
|
||||
enum class StickerFormat : int32 { Unknown, Webp, Tgs, Webm };
|
||||
|
||||
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type);
|
||||
|
||||
StickerFormat get_sticker_format_by_mime_type(Slice mime_type);
|
||||
|
||||
StickerFormat get_sticker_format_by_extension(Slice extension);
|
||||
|
@ -1304,6 +1304,21 @@ StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent
|
||||
next_update_animated_emoji_clicked_time_ = Time::now();
|
||||
}
|
||||
|
||||
StickersManager::~StickersManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), stickers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), short_name_to_sticker_set_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), attached_sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_stickers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_codes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_code_versions_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_code_last_difference_times_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), reloaded_emoji_keywords_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dice_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_messages_);
|
||||
}
|
||||
|
||||
void StickersManager::start_up() {
|
||||
init();
|
||||
}
|
||||
@ -1998,6 +2013,13 @@ double StickersManager::get_sticker_set_minithumbnail_zoom(const StickerSet *sti
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::thumbnail> StickersManager::get_sticker_set_thumbnail_object(
|
||||
const StickerSet *sticker_set) const {
|
||||
CHECK(sticker_set != nullptr);
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
return get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const {
|
||||
const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
|
||||
CHECK(sticker_set != nullptr);
|
||||
@ -2016,10 +2038,8 @@ tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(Sticke
|
||||
}
|
||||
emojis.push_back(make_tl_object<td_api::emojis>(std::move(sticker_emojis)));
|
||||
}
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
return make_tl_object<td_api::stickerSet>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_sticker_set_thumbnail_object(sticker_set),
|
||||
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2,
|
||||
get_sticker_set_minithumbnail_zoom(sticker_set)),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
@ -2091,11 +2111,9 @@ tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_obje
|
||||
}
|
||||
}
|
||||
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
auto actual_count = narrow_cast<int32>(sticker_set->sticker_ids.size());
|
||||
return make_tl_object<td_api::stickerSetInfo>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_sticker_set_thumbnail_object(sticker_set),
|
||||
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3,
|
||||
get_sticker_set_minithumbnail_zoom(sticker_set)),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
@ -5794,19 +5812,7 @@ Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_i
|
||||
return Status::Error(400, "Sticker type must be non-empty");
|
||||
}
|
||||
|
||||
switch (sticker->type_->get_id()) {
|
||||
case td_api::stickerTypeStatic::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webp, false);
|
||||
case td_api::stickerTypeAnimated::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Tgs, false);
|
||||
case td_api::stickerTypeVideo::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webm, false);
|
||||
case td_api::stickerTypeMask::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webp, false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
return prepare_input_file(sticker->sticker_, get_sticker_format(sticker->type_), false);
|
||||
}
|
||||
|
||||
Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_input_file(
|
||||
|
@ -52,6 +52,11 @@ class StickersManager final : public Actor {
|
||||
static vector<int64> convert_sticker_set_ids(const vector<StickerSetId> &sticker_set_ids);
|
||||
|
||||
StickersManager(Td *td, ActorShared<> parent);
|
||||
StickersManager(const StickersManager &) = delete;
|
||||
StickersManager &operator=(const StickersManager &) = delete;
|
||||
StickersManager(StickersManager &&) = delete;
|
||||
StickersManager &operator=(StickersManager &&) = delete;
|
||||
~StickersManager() final;
|
||||
|
||||
void init();
|
||||
|
||||
@ -500,6 +505,8 @@ class StickersManager final : public Actor {
|
||||
|
||||
static tl_object_ptr<td_api::MaskPoint> get_mask_point_object(int32 point);
|
||||
|
||||
td_api::object_ptr<td_api::thumbnail> get_sticker_set_thumbnail_object(const StickerSet *sticker_set) const;
|
||||
|
||||
tl_object_ptr<td_api::stickerSetInfo> get_sticker_set_info_object(StickerSetId sticker_set_id, size_t covers_limit,
|
||||
bool prefer_premium) const;
|
||||
|
||||
|
@ -2894,8 +2894,8 @@ bool Td::is_authentication_request(int32 id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Td::is_synchronous_request(int32 id) {
|
||||
switch (id) {
|
||||
bool Td::is_synchronous_request(const td_api::Function *function) {
|
||||
switch (function->get_id()) {
|
||||
case td_api::getTextEntities::ID:
|
||||
case td_api::parseTextEntities::ID:
|
||||
case td_api::parseMarkdown::ID:
|
||||
@ -2920,6 +2920,8 @@ bool Td::is_synchronous_request(int32 id) {
|
||||
case td_api::addLogMessage::ID:
|
||||
case td_api::testReturnError::ID:
|
||||
return true;
|
||||
case td_api::getOption::ID:
|
||||
return OptionManager::is_synchronous_option(static_cast<const td_api::getOption *>(function)->name_);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -3010,6 +3012,14 @@ td_api::object_ptr<td_api::AuthorizationState> Td::get_fake_authorization_state_
|
||||
}
|
||||
}
|
||||
|
||||
vector<td_api::object_ptr<td_api::Update>> Td::get_fake_current_state() const {
|
||||
CHECK(state_ != State::Run);
|
||||
vector<td_api::object_ptr<td_api::Update>> updates;
|
||||
OptionManager::get_common_state(updates);
|
||||
updates.push_back(td_api::make_object<td_api::updateAuthorizationState>(get_fake_authorization_state_object()));
|
||||
return updates;
|
||||
}
|
||||
|
||||
DbKey Td::as_db_key(string key, bool custom_db) {
|
||||
// Database will still be effectively not encrypted, but
|
||||
// 1. SQLite database will be protected from corruption, because that's how sqlcipher works
|
||||
@ -3036,7 +3046,7 @@ void Td::request(uint64 id, tl_object_ptr<td_api::Function> function) {
|
||||
}
|
||||
|
||||
VLOG(td_requests) << "Receive request " << id << ": " << to_string(function);
|
||||
if (is_synchronous_request(function->get_id())) {
|
||||
if (is_synchronous_request(function.get())) {
|
||||
// send response synchronously
|
||||
return send_result(id, static_request(std::move(function)));
|
||||
}
|
||||
@ -3060,14 +3070,9 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
|
||||
case td_api::getAuthorizationState::ID:
|
||||
// send response synchronously to prevent "Request aborted"
|
||||
return send_result(id, get_fake_authorization_state_object());
|
||||
case td_api::getCurrentState::ID: {
|
||||
vector<td_api::object_ptr<td_api::Update>> updates;
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"version", td_api::make_object<td_api::optionValueString>(TDLIB_VERSION)));
|
||||
updates.push_back(td_api::make_object<td_api::updateAuthorizationState>(get_fake_authorization_state_object()));
|
||||
case td_api::getCurrentState::ID:
|
||||
// send response synchronously to prevent "Request aborted"
|
||||
return send_result(id, td_api::make_object<td_api::updates>(std::move(updates)));
|
||||
}
|
||||
return send_result(id, td_api::make_object<td_api::updates>(get_fake_current_state()));
|
||||
case td_api::close::ID:
|
||||
// need to send response before actual closing
|
||||
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::ok>());
|
||||
@ -3280,10 +3285,9 @@ void Td::start_up() {
|
||||
alarm_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
|
||||
CHECK(state_ == State::WaitParameters);
|
||||
send_update(td_api::make_object<td_api::updateOption>("version",
|
||||
td_api::make_object<td_api::optionValueString>(TDLIB_VERSION)));
|
||||
send_update(td_api::make_object<td_api::updateAuthorizationState>(
|
||||
td_api::make_object<td_api::authorizationStateWaitTdlibParameters>()));
|
||||
for (auto &update : get_fake_current_state()) {
|
||||
send_update(std::move(update));
|
||||
}
|
||||
}
|
||||
|
||||
void Td::tear_down() {
|
||||
@ -3818,6 +3822,10 @@ void Td::init(Result<TdDb::OpenedDatabase> r_opened_database) {
|
||||
web_pages_manager_->on_binlog_web_page_event(std::move(event));
|
||||
}
|
||||
|
||||
for (auto &event : events.save_app_log_events) {
|
||||
on_save_app_log_binlog_event(this, std::move(event));
|
||||
}
|
||||
|
||||
if (is_online_) {
|
||||
on_online_updated(true, true);
|
||||
}
|
||||
@ -4207,6 +4215,9 @@ void Td::send_error_impl(uint64 id, tl_object_ptr<td_api::error> error) {
|
||||
if (it != request_set_.end()) {
|
||||
request_set_.erase(it);
|
||||
VLOG(td_requests) << "Sending error for request " << id << ": " << oneline(to_string(error));
|
||||
if (error->code_ == 0 && error->message_ == "Lost promise") {
|
||||
LOG(FATAL) << "Lost promise for query " << id;
|
||||
}
|
||||
callback_->on_error(id, std::move(error));
|
||||
}
|
||||
}
|
||||
@ -4342,7 +4353,9 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
|
||||
}
|
||||
if (options_.api_id != 21724) {
|
||||
options_.application_version += ", TDLib ";
|
||||
options_.application_version += TDLIB_VERSION;
|
||||
auto version = OptionManager::get_option_synchronously("version");
|
||||
CHECK(version->get_id() == td_api::optionValueString::ID);
|
||||
options_.application_version += static_cast<const td_api::optionValueString *>(version.get())->value_;
|
||||
}
|
||||
options_.language_pack = string();
|
||||
options_.language_code = string();
|
||||
@ -8235,6 +8248,13 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &
|
||||
return get_formatted_text_object(parsed_text, false, std::numeric_limits<int32>::max());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getOption &request) {
|
||||
if (!is_synchronous_request(&request)) {
|
||||
return make_error(400, "The option can't be get synchronously");
|
||||
}
|
||||
return OptionManager::get_option_synchronously(request.name_);
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText &request) {
|
||||
if (request.text_ == nullptr) {
|
||||
return make_error(400, "Text must be non-empty");
|
||||
@ -8459,6 +8479,4 @@ void Td::on_request(uint64 id, td_api::testCallVectorStringObject &request) {
|
||||
#undef CREATE_REQUEST_PROMISE
|
||||
#undef CREATE_OK_REQUEST_PROMISE
|
||||
|
||||
constexpr const char *Td::TDLIB_VERSION;
|
||||
|
||||
} // namespace td
|
||||
|
@ -109,8 +109,6 @@ class Td final : public Actor {
|
||||
Td &operator=(Td &&) = delete;
|
||||
~Td() final;
|
||||
|
||||
static constexpr const char *TDLIB_VERSION = "1.8.4";
|
||||
|
||||
struct Options {
|
||||
std::shared_ptr<NetQueryStats> net_query_stats;
|
||||
};
|
||||
@ -339,6 +337,8 @@ class Td final : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::AuthorizationState> get_fake_authorization_state_object() const;
|
||||
|
||||
vector<td_api::object_ptr<td_api::Update>> get_fake_current_state() const;
|
||||
|
||||
static void on_alarm_timeout_callback(void *td_ptr, int64 alarm_id);
|
||||
void on_alarm_timeout(int64 alarm_id);
|
||||
|
||||
@ -389,7 +389,7 @@ class Td final : public Actor {
|
||||
|
||||
static bool is_authentication_request(int32 id);
|
||||
|
||||
static bool is_synchronous_request(int32 id);
|
||||
static bool is_synchronous_request(const td_api::Function *function);
|
||||
|
||||
static bool is_preinitialization_request(int32 id);
|
||||
|
||||
@ -1419,6 +1419,7 @@ class Td final : public Actor {
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const T &request) {
|
||||
return td_api::make_object<td_api::error>(400, "The method can't be executed synchronously");
|
||||
}
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getOption &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getTextEntities &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::parseTextEntities &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::parseMarkdown &request);
|
||||
|
@ -117,6 +117,9 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue<Binlog> &binlog_p
|
||||
case LogEvent::HandlerType::EditMessagePushNotification:
|
||||
events.to_notification_manager.push_back(event.clone());
|
||||
break;
|
||||
case LogEvent::HandlerType::SaveAppLog:
|
||||
events.save_app_log_events.push_back(event.clone());
|
||||
break;
|
||||
case LogEvent::HandlerType::BinlogPmcMagic:
|
||||
binlog_pmc.external_init_handle(event);
|
||||
break;
|
||||
|
@ -63,6 +63,7 @@ class TdDb {
|
||||
vector<BinlogEvent> channel_events;
|
||||
vector<BinlogEvent> secret_chat_events;
|
||||
vector<BinlogEvent> web_page_events;
|
||||
vector<BinlogEvent> save_app_log_events;
|
||||
vector<BinlogEvent> to_poll_manager;
|
||||
vector<BinlogEvent> to_messages_manager;
|
||||
vector<BinlogEvent> to_notification_manager;
|
||||
|
@ -23,6 +23,10 @@ namespace td {
|
||||
VideoNotesManager::VideoNotesManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
VideoNotesManager::~VideoNotesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), video_notes_);
|
||||
}
|
||||
|
||||
int32 VideoNotesManager::get_video_note_duration(FileId file_id) const {
|
||||
auto it = video_notes_.find(file_id);
|
||||
CHECK(it != video_notes_.end());
|
||||
|
@ -24,6 +24,11 @@ class Td;
|
||||
class VideoNotesManager {
|
||||
public:
|
||||
explicit VideoNotesManager(Td *td);
|
||||
VideoNotesManager(const VideoNotesManager &) = delete;
|
||||
VideoNotesManager &operator=(const VideoNotesManager &) = delete;
|
||||
VideoNotesManager(VideoNotesManager &&) = delete;
|
||||
VideoNotesManager &operator=(VideoNotesManager &&) = delete;
|
||||
~VideoNotesManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -24,6 +24,10 @@ namespace td {
|
||||
VideosManager::VideosManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
VideosManager::~VideosManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), videos_);
|
||||
}
|
||||
|
||||
int32 VideosManager::get_video_duration(FileId file_id) const {
|
||||
auto it = videos_.find(file_id);
|
||||
CHECK(it != videos_.end());
|
||||
|
@ -24,6 +24,11 @@ class Td;
|
||||
class VideosManager {
|
||||
public:
|
||||
explicit VideosManager(Td *td);
|
||||
VideosManager(const VideosManager &) = delete;
|
||||
VideosManager &operator=(const VideosManager &) = delete;
|
||||
VideosManager(VideosManager &&) = delete;
|
||||
VideosManager &operator=(VideosManager &&) = delete;
|
||||
~VideosManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -100,6 +100,12 @@ VoiceNotesManager::VoiceNotesManager(Td *td, ActorShared<> parent) : td_(td), pa
|
||||
voice_note_transcription_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
VoiceNotesManager::~VoiceNotesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), voice_notes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), voice_note_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_voice_notes_);
|
||||
}
|
||||
|
||||
void VoiceNotesManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ class Td;
|
||||
class VoiceNotesManager final : public Actor {
|
||||
public:
|
||||
VoiceNotesManager(Td *td, ActorShared<> parent);
|
||||
VoiceNotesManager(const VoiceNotesManager &) = delete;
|
||||
VoiceNotesManager &operator=(const VoiceNotesManager &) = delete;
|
||||
VoiceNotesManager(VoiceNotesManager &&) = delete;
|
||||
VoiceNotesManager &operator=(VoiceNotesManager &&) = delete;
|
||||
~VoiceNotesManager() final;
|
||||
|
||||
int32 get_voice_note_duration(FileId file_id) const;
|
||||
|
||||
|
@ -417,7 +417,13 @@ void WebPagesManager::tear_down() {
|
||||
LOG(DEBUG) << "Have " << web_pages_.size() << " web pages to free";
|
||||
}
|
||||
|
||||
WebPagesManager::~WebPagesManager() = default;
|
||||
WebPagesManager::~WebPagesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), web_pages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), web_page_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), got_web_page_previews_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), url_to_web_page_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), url_to_file_source_id_);
|
||||
}
|
||||
|
||||
WebPageId WebPagesManager::on_get_web_page(tl_object_ptr<telegram_api::WebPage> &&web_page_ptr,
|
||||
DialogId owner_dialog_id) {
|
||||
|
@ -2401,6 +2401,8 @@ class CliClient final : public Actor {
|
||||
td_api::make_object<td_api::optionValueBoolean>(op == "on")));
|
||||
} else if (op == "go") {
|
||||
send_request(td_api::make_object<td_api::getOption>(args));
|
||||
} else if (op == "gos") {
|
||||
execute(td_api::make_object<td_api::getOption>(args));
|
||||
} else if (op == "sob") {
|
||||
string name;
|
||||
bool value;
|
||||
|
@ -841,6 +841,14 @@ void FileManager::init_actor() {
|
||||
}
|
||||
|
||||
FileManager::~FileManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), remote_location_info_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_hash_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), local_location_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), generate_location_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), pmc_id_to_file_node_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_id_info_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), empty_file_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_nodes_);
|
||||
}
|
||||
|
||||
string FileManager::fix_file_extension(Slice file_name, Slice file_type, Slice file_extension) {
|
||||
@ -1009,6 +1017,13 @@ static Status check_partial_local_location(const PartialLocalFileLocation &locat
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void FileManager::check_local_location(FileId file_id) {
|
||||
auto node = get_sync_file_node(file_id);
|
||||
if (node) {
|
||||
check_local_location(node).ignore();
|
||||
}
|
||||
}
|
||||
|
||||
Status FileManager::check_local_location(FileNodePtr node) {
|
||||
Status status;
|
||||
if (node->local_.type() == LocalFileLocation::Type::Full) {
|
||||
@ -1100,7 +1115,6 @@ FileId FileManager::register_empty(FileType type) {
|
||||
}
|
||||
|
||||
void FileManager::on_file_unlink(const FullLocalFileLocation &location) {
|
||||
// TODO: remove file from the database too
|
||||
auto it = local_location_to_file_id_.find(location);
|
||||
if (it == local_location_to_file_id_.end()) {
|
||||
return;
|
||||
@ -1108,6 +1122,7 @@ void FileManager::on_file_unlink(const FullLocalFileLocation &location) {
|
||||
auto file_id = it->second;
|
||||
auto file_node = get_sync_file_node(file_id);
|
||||
CHECK(file_node);
|
||||
clear_from_pmc(file_node);
|
||||
send_closure(G()->download_manager(), &DownloadManager::remove_file_if_finished, file_node->main_file_id_);
|
||||
file_node->drop_local_location();
|
||||
try_flush_node_info(file_node, "on_file_unlink");
|
||||
|
@ -449,6 +449,8 @@ class FileManager final : public FileLoadManager::Callback {
|
||||
bool set_encryption_key(FileId file_id, FileEncryptionKey key);
|
||||
bool set_content(FileId file_id, BufferSlice bytes);
|
||||
|
||||
void check_local_location(FileId file_id);
|
||||
|
||||
void download(FileId file_id, std::shared_ptr<DownloadCallback> callback, int32 new_priority, int64 offset,
|
||||
int64 limit);
|
||||
void upload(FileId file_id, std::shared_ptr<UploadCallback> callback, int32 new_priority, uint64 upload_order);
|
||||
|
@ -224,9 +224,9 @@ FileId FileManager::parse_file(ParserT &parser) {
|
||||
if (expected_size < 0) {
|
||||
expected_size += static_cast<int64>(1) << 32;
|
||||
}
|
||||
}
|
||||
int32 zero;
|
||||
parse(zero, parser);
|
||||
}
|
||||
DialogId owner_dialog_id;
|
||||
if (parser.version() >= static_cast<int32>(Version::StoreFileOwnerId)) {
|
||||
parse(owner_dialog_id, parser);
|
||||
|
@ -104,6 +104,7 @@ class LogEvent {
|
||||
GetChannelDifference = 0x140,
|
||||
AddMessagePushNotification = 0x200,
|
||||
EditMessagePushNotification = 0x201,
|
||||
SaveAppLog = 0x300,
|
||||
ConfigPmcMagic = 0x1f18,
|
||||
BinlogPmcMagic = 0x4327
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
||||
@ -194,7 +194,7 @@ Status NetQueryDispatcher::wait_dc_init(DcId dc_id, bool force) {
|
||||
return Status::Error("Closing");
|
||||
}
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -331,7 +331,7 @@ void NetQueryDispatcher::set_main_dc_id(int32 new_main_dc_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Very rare event. Mutex is ok.
|
||||
// Very rare event; mutex is ok.
|
||||
std::lock_guard<std::mutex> guard(main_dc_id_mutex_);
|
||||
if (new_main_dc_id == main_dc_id_) {
|
||||
return;
|
||||
|
@ -69,11 +69,6 @@ class SemaphoreActor final : public Actor {
|
||||
size_t capacity_;
|
||||
VectorQueue<Promise<Promise<Unit>>> pending_;
|
||||
|
||||
void start_up() final {
|
||||
set_context(std::make_shared<ActorContext>());
|
||||
set_tag(string());
|
||||
}
|
||||
|
||||
void finish(Result<Unit>) {
|
||||
capacity_++;
|
||||
if (!pending_.empty()) {
|
||||
@ -137,7 +132,11 @@ class GenAuthKeyActor final : public Actor {
|
||||
|
||||
static TD_THREAD_LOCAL Semaphore *semaphore_;
|
||||
Semaphore &get_handshake_semaphore() {
|
||||
auto old_context = set_context(std::make_shared<ActorContext>());
|
||||
auto old_tag = set_tag(string());
|
||||
init_thread_local<Semaphore>(semaphore_, 50);
|
||||
set_context(std::move(old_context));
|
||||
set_tag(std::move(old_tag));
|
||||
return *semaphore_;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ TDJSON_EXPORT const char *td_execute(const char *request);
|
||||
/**
|
||||
* A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
*
|
||||
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024).
|
||||
* \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* \param message Null-terminated UTF-8-encoded string with the message added to the log.
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class Timeout final : public Actor {
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "td/utils/Closure.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <type_traits>
|
||||
@ -49,7 +48,6 @@ class CustomEvent {
|
||||
virtual ~CustomEvent() = default;
|
||||
|
||||
virtual void run(Actor *actor) = 0;
|
||||
virtual CustomEvent *clone() const = 0;
|
||||
virtual void start_migrate(int32 sched_id) {
|
||||
}
|
||||
virtual void finish_migrate() {
|
||||
@ -62,9 +60,6 @@ class ClosureEvent final : public CustomEvent {
|
||||
void run(Actor *actor) final {
|
||||
closure_.run(static_cast<typename ClosureT::ActorType *>(actor));
|
||||
}
|
||||
CustomEvent *clone() const final {
|
||||
return new ClosureEvent<ClosureT>(closure_.clone());
|
||||
}
|
||||
template <class... ArgsT>
|
||||
explicit ClosureEvent(ArgsT &&...args) : closure_(std::forward<ArgsT>(args)...) {
|
||||
}
|
||||
@ -93,10 +88,6 @@ class LambdaEvent final : public CustomEvent {
|
||||
void run(Actor *actor) final {
|
||||
f_();
|
||||
}
|
||||
CustomEvent *clone() const final {
|
||||
LOG(FATAL) << "Not supported";
|
||||
return nullptr;
|
||||
}
|
||||
template <class FromLambdaT, std::enable_if_t<!std::is_same<std::decay_t<FromLambdaT>, LambdaEvent>::value, int> = 0>
|
||||
explicit LambdaEvent(FromLambdaT &&lambda) : f_(std::forward<FromLambdaT>(lambda)) {
|
||||
}
|
||||
@ -181,17 +172,6 @@ class Event {
|
||||
destroy();
|
||||
}
|
||||
|
||||
Event clone() const {
|
||||
Event res;
|
||||
res.type = type;
|
||||
if (type == Type::Custom) {
|
||||
res.data.custom_event = data.custom_event->clone();
|
||||
} else {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return type == Type::NoType;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "td/actor/impl/EventFull-decl.h"
|
||||
|
||||
#include "td/utils/Closure.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/Heap.h"
|
||||
#include "td/utils/List.h"
|
||||
@ -101,6 +102,9 @@ class Scheduler {
|
||||
|
||||
void run_on_scheduler(int32 sched_id, Promise<Unit> action); // TODO Action
|
||||
|
||||
template <class T>
|
||||
void destroy_on_scheduler(int32 sched_id, T &value);
|
||||
|
||||
template <ActorSendType send_type, class EventT>
|
||||
void send_lambda(ActorRef actor_ref, EventT &&lambda);
|
||||
|
||||
@ -149,6 +153,8 @@ class Scheduler {
|
||||
private:
|
||||
static void set_scheduler(Scheduler *scheduler);
|
||||
|
||||
void destroy_on_scheduler_impl(int32 sched_id, Promise<Unit> action);
|
||||
|
||||
class ServiceActor final : public Actor {
|
||||
public:
|
||||
void set_queue(std::shared_ptr<MpscPollableQueue<EventFull>> queues);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "td/utils/MpscPollableQueue.h"
|
||||
#include "td/utils/ObjectPool.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
@ -361,6 +362,21 @@ void Scheduler::run_on_scheduler(int32 sched_id, Promise<Unit> action) {
|
||||
action.set_value(Unit());
|
||||
}
|
||||
|
||||
void Scheduler::destroy_on_scheduler_impl(int32 sched_id, Promise<Unit> action) {
|
||||
auto empty_context = std::make_shared<ActorContext>();
|
||||
empty_context->this_ptr_ = empty_context;
|
||||
ActorContext *current_context = context_;
|
||||
context_ = empty_context.get();
|
||||
|
||||
const char *current_tag = LOG_TAG;
|
||||
LOG_TAG = nullptr;
|
||||
|
||||
run_on_scheduler(sched_id, std::move(action));
|
||||
|
||||
context_ = current_context;
|
||||
LOG_TAG = current_tag;
|
||||
}
|
||||
|
||||
void Scheduler::add_to_mailbox(ActorInfo *actor_info, Event &&event) {
|
||||
if (!actor_info->is_running()) {
|
||||
auto node = actor_info->get_list_node();
|
||||
|
@ -161,7 +161,7 @@ void Scheduler::flush_mailbox(ActorInfo *actor_info, const RunFuncT &run_func, c
|
||||
mailbox.erase(mailbox.begin(), mailbox.begin() + i);
|
||||
}
|
||||
|
||||
inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<> &actor_id, Event &&event) {
|
||||
inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<Actor> &actor_id, Event &&event) {
|
||||
if (sched_id == sched_id_) {
|
||||
ActorInfo *actor_info = actor_id.get_actor_info();
|
||||
pending_events_[actor_info].push_back(std::move(event));
|
||||
@ -170,6 +170,15 @@ inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<> &actor_
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Scheduler::destroy_on_scheduler(int32 sched_id, T &value) {
|
||||
if (!value.empty()) {
|
||||
destroy_on_scheduler_impl(sched_id, PromiseCreator::lambda([value = std::move(value)](Unit) {
|
||||
// destroy value
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
inline void Scheduler::before_tail_send(const ActorId<> &actor_id) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -98,6 +98,11 @@ long strm_ctrl(BIO *b, int cmd, long num, void *ptr) {
|
||||
case BIO_CTRL_PUSH:
|
||||
case BIO_CTRL_POP:
|
||||
return 0;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
case BIO_CTRL_GET_KTLS_SEND:
|
||||
case BIO_CTRL_GET_KTLS_RECV:
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
LOG(FATAL) << b << " " << cmd << " " << num << " " << ptr;
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/port/IPAddress.h
|
||||
td/utils/port/IoSlice.h
|
||||
td/utils/port/MemoryMapping.h
|
||||
td/utils/port/Mutex.h
|
||||
td/utils/port/path.h
|
||||
td/utils/port/platform.h
|
||||
td/utils/port/Poll.h
|
||||
@ -337,7 +338,6 @@ set(TDUTILS_TEST_SOURCE
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
#RULES
|
||||
#LIBRARIES
|
||||
add_library(tdutils STATIC ${TDUTILS_SOURCE})
|
||||
|
||||
|
@ -3,7 +3,7 @@ if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.0.2"))
|
||||
endif()
|
||||
|
||||
# Generates files for MIME type <-> extension conversions
|
||||
# DEPENDS ON: gperf grep bash/powershell
|
||||
# DEPENDS ON: gperf grep
|
||||
|
||||
if (NOT TDUTILS_MIME_TYPE)
|
||||
return()
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/type_traits.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -20,6 +20,15 @@ template <class T>
|
||||
class AtomicRead {
|
||||
public:
|
||||
void read(T &dest) const {
|
||||
uint32 counter = 0;
|
||||
auto wait = [&] {
|
||||
counter++;
|
||||
const int wait_each_count = 4;
|
||||
if (counter % wait_each_count == 0) {
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
static_assert(TD_IS_TRIVIALLY_COPYABLE(T), "T must be trivially copyable");
|
||||
auto version_before = version.load();
|
||||
@ -30,7 +39,7 @@ class AtomicRead {
|
||||
break;
|
||||
}
|
||||
}
|
||||
td::this_thread::yield();
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,7 @@
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/invoke.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -55,7 +53,6 @@
|
||||
//
|
||||
//
|
||||
// create_immediate_closure(&ActorT::func, arg1, arg2, ..., argn).run(actor)
|
||||
// to_delayed_closure(std::move(immediate)).run(actor)
|
||||
|
||||
namespace td {
|
||||
template <class ActorT, class FunctionT, class... ArgsT>
|
||||
@ -92,11 +89,6 @@ template <class ActorT, class FunctionT, class... ArgsT>
|
||||
class DelayedClosure {
|
||||
public:
|
||||
using ActorType = ActorT;
|
||||
using Delayed = DelayedClosure<ActorT, FunctionT, ArgsT...>;
|
||||
|
||||
DelayedClosure clone() const {
|
||||
return do_clone(*this);
|
||||
}
|
||||
|
||||
explicit DelayedClosure(ImmediateClosure<ActorT, FunctionT, ArgsT...> &&other) : args(std::move(other.args)) {
|
||||
}
|
||||
@ -110,38 +102,7 @@ class DelayedClosure {
|
||||
}
|
||||
|
||||
private:
|
||||
using ArgsStorageT = std::tuple<FunctionT, typename std::decay<ArgsT>::type...>;
|
||||
|
||||
ArgsStorageT args;
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
explicit DelayedClosure(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
|
||||
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0)
|
||||
: args(other.args) {
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
explicit DelayedClosure(
|
||||
const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
|
||||
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0) {
|
||||
LOG(FATAL) << "Deleted constructor";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
|
||||
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
|
||||
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
|
||||
LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
|
||||
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
|
||||
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
|
||||
return DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>(value);
|
||||
}
|
||||
std::tuple<FunctionT, typename std::decay<ArgsT>::type...> args;
|
||||
|
||||
public:
|
||||
auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
|
||||
@ -149,16 +110,6 @@ class DelayedClosure {
|
||||
}
|
||||
};
|
||||
|
||||
template <class... ArgsT>
|
||||
typename ImmediateClosure<ArgsT...>::Delayed to_delayed_closure(ImmediateClosure<ArgsT...> &&other) {
|
||||
return typename ImmediateClosure<ArgsT...>::Delayed(std::move(other));
|
||||
}
|
||||
|
||||
template <class... ArgsT>
|
||||
DelayedClosure<ArgsT...> to_delayed_closure(DelayedClosure<ArgsT...> &&other) {
|
||||
return std::move(other);
|
||||
}
|
||||
|
||||
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
|
||||
auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&...args) {
|
||||
return DelayedClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
|
||||
|
@ -42,6 +42,10 @@ class Enumerator {
|
||||
return arr_.size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<ValueT, int32> map_;
|
||||
std::vector<const ValueT *> arr_;
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/HazardPointers.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
#include <array>
|
||||
@ -305,7 +305,7 @@ class MpmcQueueOld {
|
||||
if (try_pop(value, thread_id)) {
|
||||
return value;
|
||||
}
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +429,7 @@ class MpmcQueue {
|
||||
if (try_pop(value, thread_id)) {
|
||||
return value;
|
||||
}
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@ -29,16 +29,17 @@ class MpmcEagerWaiter {
|
||||
slot.yields = 0;
|
||||
slot.worker_id = worker_id;
|
||||
}
|
||||
|
||||
void wait(Slot &slot) {
|
||||
if (slot.yields < RoundsTillSleepy) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
} else if (slot.yields == RoundsTillSleepy) {
|
||||
auto state = state_.load(std::memory_order_relaxed);
|
||||
if (!State::has_worker(state)) {
|
||||
auto new_state = State::with_worker(state, slot.worker_id);
|
||||
if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
@ -47,12 +48,12 @@ class MpmcEagerWaiter {
|
||||
return;
|
||||
}
|
||||
}
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields = 0;
|
||||
} else if (slot.yields < RoundsTillAsleep) {
|
||||
auto state = state_.load(std::memory_order_acquire);
|
||||
if (State::still_sleepy(state, slot.worker_id)) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
@ -121,6 +122,10 @@ class MpmcEagerWaiter {
|
||||
condition_variable_.notify_all();
|
||||
}
|
||||
}
|
||||
static void yield() {
|
||||
// whatever, this is better than sched_yield
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
class MpmcSleepyWaiter {
|
||||
@ -208,7 +213,7 @@ class MpmcSleepyWaiter {
|
||||
}
|
||||
if (slot.state_ == Slot::Search) {
|
||||
if (slot.yield_cnt++ < 10 && false) {
|
||||
td::this_thread::yield();
|
||||
// TODO some sleep backoff is possible
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -91,7 +91,7 @@ class MpscPollableQueue {
|
||||
}
|
||||
|
||||
private:
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
bool wait_event_fd_{false};
|
||||
EventFd event_fd_;
|
||||
std::vector<ValueType> writer_vector_;
|
||||
|
@ -6,7 +6,7 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -30,7 +30,7 @@ class SpinLock {
|
||||
//TODO pause
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,31 @@ class WaitFreeHashMap {
|
||||
size_t erase(const KeyT &key) {
|
||||
return get_storage(key).erase(key);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
if (wait_free_storage_ == nullptr) {
|
||||
return default_map_.size();
|
||||
}
|
||||
|
||||
size_t result = 0;
|
||||
for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
|
||||
result += wait_free_storage_->maps_[i].size();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
if (wait_free_storage_ == nullptr) {
|
||||
return default_map_.empty();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
|
||||
if (!wait_free_storage_->maps_[i].empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
30
tdutils/td/utils/port/Mutex.h
Normal file
30
tdutils/td/utils/port/Mutex.h
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
struct Guard {
|
||||
std::unique_lock<std::mutex> guard;
|
||||
void reset() {
|
||||
guard.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
Guard lock() {
|
||||
return {std::unique_lock<std::mutex>(mutex_)};
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace td
|
@ -29,7 +29,7 @@
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/VectorQueue.h"
|
||||
#endif
|
||||
|
||||
@ -87,7 +87,7 @@ class ServerSocketFdImpl final : private Iocp::Callback {
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
VectorQueue<SocketFd> accepted_;
|
||||
VectorQueue<Status> pending_errors_;
|
||||
static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16;
|
||||
|
@ -17,7 +17,7 @@
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/VectorQueue.h"
|
||||
|
||||
#include <limits>
|
||||
@ -164,7 +164,7 @@ class SocketFdImpl final : private Iocp::Callback {
|
||||
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
|
||||
std::atomic<int> refcnt_{1};
|
||||
bool close_flag_{false};
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
@ -154,7 +154,7 @@ class UdpSocketFdImpl final : private Iocp::Callback {
|
||||
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
|
||||
std::atomic<int> refcnt_{1};
|
||||
bool is_connected_{false};
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Observer.h"
|
||||
#include "td/utils/port/detail/NativeFd.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -140,7 +140,7 @@ class PollableFdInfo final : private ListNode {
|
||||
std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
|
||||
PollFlagsSet flags_;
|
||||
#if TD_PORT_WINDOWS
|
||||
SpinLock observer_lock_;
|
||||
Mutex observer_lock_;
|
||||
#endif
|
||||
ObserverBase *observer_{nullptr};
|
||||
|
||||
|
@ -88,9 +88,6 @@ int ThreadPthread::do_pthread_create(pthread_t *thread, const pthread_attr_t *at
|
||||
}
|
||||
|
||||
namespace this_thread_pthread {
|
||||
void yield() {
|
||||
sched_yield();
|
||||
}
|
||||
ThreadPthread::id get_id() {
|
||||
return pthread_self();
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ class ThreadPthread {
|
||||
};
|
||||
|
||||
namespace this_thread_pthread {
|
||||
void yield();
|
||||
ThreadPthread::id get_id();
|
||||
} // namespace this_thread_pthread
|
||||
} // namespace detail
|
||||
|
@ -70,7 +70,9 @@ class ThreadStl {
|
||||
return std::forward<T>(v);
|
||||
}
|
||||
};
|
||||
namespace this_thread_stl = std::this_thread;
|
||||
namespace this_thread_stl {
|
||||
using std::this_thread::get_id;
|
||||
} // namespace this_thread_stl
|
||||
} // namespace detail
|
||||
} // namespace td
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/EventFd.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
|
||||
@ -32,7 +32,7 @@ class Backoff {
|
||||
if (cnt < 1) { // 50
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return cnt < 3; // 500
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ class InfBackoff {
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,14 @@
|
||||
#include "td/utils/Context.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace td {
|
||||
@ -126,12 +128,12 @@ class RegisterTest {
|
||||
}
|
||||
};
|
||||
|
||||
class Stage {
|
||||
class StageWait {
|
||||
public:
|
||||
void wait(uint64 need) {
|
||||
value_.fetch_add(1, std::memory_order_release);
|
||||
while (value_.load(std::memory_order_acquire) < need) {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -139,6 +141,26 @@ class Stage {
|
||||
std::atomic<uint64> value_{0};
|
||||
};
|
||||
|
||||
class StageMutex {
|
||||
public:
|
||||
void wait(uint64 need) {
|
||||
std::unique_lock<std::mutex> lock{mutex_};
|
||||
value_++;
|
||||
if (value_ == need) {
|
||||
cond_.notify_all();
|
||||
return;
|
||||
}
|
||||
cond_.wait(lock, [&] { return value_ >= need; });
|
||||
};
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_;
|
||||
uint64 value_{0};
|
||||
};
|
||||
|
||||
using Stage = StageMutex;
|
||||
|
||||
string rand_string(int from, int to, size_t len);
|
||||
|
||||
vector<string> rand_split(Slice str);
|
||||
|
@ -7,12 +7,12 @@
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/ConcurrentHashTable.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
|
||||
@ -72,11 +72,11 @@ class ConcurrentHashMapMutex {
|
||||
return "ConcurrentHashMapMutex";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
auto guard = mutex_.lock();
|
||||
hash_map_.emplace(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
auto guard = mutex_.lock();
|
||||
auto it = hash_map_.find(key);
|
||||
if (it == hash_map_.end()) {
|
||||
return default_value;
|
||||
@ -85,7 +85,7 @@ class ConcurrentHashMapMutex {
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
Mutex mutex_;
|
||||
#if TD_HAVE_ABSL
|
||||
absl::flat_hash_map<KeyT, ValueT> hash_map_;
|
||||
#else
|
||||
|
@ -265,8 +265,7 @@ TEST(FlatHashMap, stress_test) {
|
||||
|
||||
td::vector<td::RandomSteps::Step> steps;
|
||||
auto add_step = [&](td::Slice step_name, td::uint32 weight, auto f) {
|
||||
auto g = [&, step_name, f = std::move(f)] {
|
||||
//LOG(ERROR) << step_name;
|
||||
auto g = [&, f = std::move(f)] {
|
||||
//ASSERT_EQ(ref.size(), tbl.size());
|
||||
f();
|
||||
ASSERT_EQ(ref.size(), tbl.size());
|
||||
|
@ -30,6 +30,8 @@ TEST(WaitFreeHashMap, stress_test) {
|
||||
reference[key] = value;
|
||||
map.set(key, value);
|
||||
ASSERT_EQ(reference[key], map.get(key));
|
||||
ASSERT_EQ(reference.size(), map.size());
|
||||
ASSERT_EQ(reference.empty(), map.empty());
|
||||
});
|
||||
|
||||
add_step(2000, [&] {
|
||||
@ -37,6 +39,8 @@ TEST(WaitFreeHashMap, stress_test) {
|
||||
auto ref_it = reference.find(key);
|
||||
auto ref_value = ref_it == reference.end() ? 0 : ref_it->second;
|
||||
ASSERT_EQ(ref_value, map.get(key));
|
||||
ASSERT_EQ(reference.size(), map.size());
|
||||
ASSERT_EQ(reference.empty(), map.empty());
|
||||
});
|
||||
|
||||
add_step(500, [&] {
|
||||
@ -44,6 +48,8 @@ TEST(WaitFreeHashMap, stress_test) {
|
||||
size_t reference_erased_count = reference.erase(key);
|
||||
size_t map_erased_count = map.erase(key);
|
||||
ASSERT_EQ(reference_erased_count, map_erased_count);
|
||||
ASSERT_EQ(reference.size(), map.size());
|
||||
ASSERT_EQ(reference.empty(), map.empty());
|
||||
});
|
||||
|
||||
td::RandomSteps runner(std::move(steps));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user