Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-07-22 13:09:22 +02:00
commit cd4a07a7c7
102 changed files with 1079 additions and 434 deletions

View File

@ -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")

View 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()

View 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()

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -51,7 +51,7 @@ class Backoff {
if (cnt < 50) {
return true;
} else {
td::this_thread::yield();
td::usleep_for(1);
return cnt < 500;
}
}

View File

@ -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 {

View File

@ -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>

View File

@ -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
)

View File

@ -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);
}

View File

@ -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();
}
}
}
}

View File

@ -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"));
@ -377,7 +381,7 @@ public final class Example {
TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object;
users.put(updateUser.user.id, updateUser.user);
break;
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object;
TdApi.User user = users.get(updateUserStatus.userId);
synchronized (user) {
@ -438,7 +442,7 @@ public final class Example {
case TdApi.UpdateChatPosition.CONSTRUCTOR: {
TdApi.UpdateChatPosition updateChat = (TdApi.UpdateChatPosition) object;
if (updateChat.position.list.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
break;
break;
}
TdApi.Chat chat = chats.get(updateChat.chatId);
@ -452,7 +456,7 @@ public final class Example {
TdApi.ChatPosition[] new_positions = new TdApi.ChatPosition[chat.positions.length + (updateChat.position.order == 0 ? 0 : 1) - (i < chat.positions.length ? 1 : 0)];
int pos = 0;
if (updateChat.position.order != 0) {
new_positions[pos++] = updateChat.position;
new_positions[pos++] = updateChat.position;
}
for (int j = 0; j < chat.positions.length; j++) {
if (j != i) {
@ -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();
}
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -4814,7 +4814,7 @@ searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:
//@filter Additional filter for messages to search; pass null to search for all messages
searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter:SearchMessagesFilter = FoundMessages;
//@description Searches for call messages. Returns the results in reverse chronological order (i. e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
//@description Searches for call messages. Returns the results in reverse chronological order (i.e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
//@from_message_id Identifier of the message from which to search; use 0 to get results from the last message
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
//@only_missed Pass true to search only for messages with missed/declined calls
@ -6102,7 +6102,7 @@ getSupergroupMembers supergroup_id:int53 filter:SupergroupMembersFilter offset:i
closeSecretChat secret_chat_id:int32 = Ok;
//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only for supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i. e., in order of decreasing event_id)
//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only for supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i.e., in order of decreasing event_id)
//@chat_id Chat identifier @query Search query by which to filter events @from_event_id Identifier of an event from which to return results. Use 0 to get results from the latest events @limit The maximum number of events to return; up to 100
//@filters The types of events to return; pass null to get chat events of all types @user_ids User identifiers by which to filter events. By default, events relating to all users will be returned
getChatEventLog chat_id:int53 query:string from_event_id:int64 limit:int32 filters:chatEventLogFilters user_ids:vector<int53> = ChatEvents;
@ -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;

View File

@ -26,6 +26,7 @@ inputStickerSetThumbLegacy#dbaeae9 stickerset:InputStickerSet volume_id:long loc
test.useError = Error;
test.useConfigSimple = help.ConfigSimple;
test.parseInputAppEvent = InputAppEvent;
---types---

View File

@ -21,7 +21,7 @@ Status check_message_id_duplicates(int64 *saved_message_ids, size_t max_size, si
// In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if
// a message comes in with msg_id lower than all or equal to any of the stored values, that message is to be
// ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is
// greater than N, the oldest (i. e. the lowest) is forgotten.
// greater than N, the oldest (i.e. the lowest) is forgotten.
if (end_pos == 2 * max_size) {
std::copy_n(&saved_message_ids[max_size], max_size, &saved_message_ids[0]);
end_pos = max_size;

View File

@ -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();
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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);

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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>(

View File

@ -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()) {

View File

@ -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:

View File

@ -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;

View File

@ -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(

View File

@ -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_");
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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_) {

View File

@ -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);

View File

@ -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;

View File

@ -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,15 +1655,13 @@ 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) {
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
auto length = narrow_cast<int32>(url.first.size());
entities.emplace_back(type, offset, length);
}
add_entities(MessageEntity::Type::Url, find_tg_urls);
auto urls = find_urls(text);
for (auto &url : urls) {
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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> &notification) {
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));
}

View File

@ -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())));

View File

@ -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);

View File

@ -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()) {

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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(

View 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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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);
}
int32 zero;
parse(zero, parser);
DialogId owner_dialog_id;
if (parser.version() >= static_cast<int32>(Version::StoreFileOwnerId)) {
parse(owner_dialog_id, parser);

View File

@ -104,6 +104,7 @@ class LogEvent {
GetChannelDifference = 0x140,
AddMessagePushNotification = 0x200,
EditMessagePushNotification = 0x201,
SaveAppLog = 0x300,
ConfigPmcMagic = 0x1f18,
BinlogPmcMagic = 0x4327
};

View File

@ -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;

View File

@ -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_;
}

View File

@ -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.

View File

@ -8,6 +8,8 @@
#include "td/actor/actor.h"
#include "td/utils/common.h"
namespace td {
class Timeout final : public Actor {

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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
}

View File

@ -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;
}

View File

@ -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})

View File

@ -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()
@ -25,7 +25,7 @@ add_custom_target(tdmime_auto DEPENDS ${TDMIME_SOURCE})
if (NOT CMAKE_CROSSCOMPILING)
find_program(GPERF_EXECUTABLE gperf)
if (NOT GPERF_EXECUTABLE)
message(FATAL_ERROR "Could NOT find gperf. Add path to gperf executable to PATH environment variable or specify it manually using GPERF_EXECUTABLE option, i. e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\"'.")
message(FATAL_ERROR "Could NOT find gperf. Add path to gperf executable to PATH environment variable or specify it manually using GPERF_EXECUTABLE option, i.e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\"'.")
endif()
set(GPERF_FILES

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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_;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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_;

View File

@ -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;
}
}

View File

@ -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

View 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

View File

@ -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;

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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();
}

View File

@ -83,7 +83,6 @@ class ThreadPthread {
};
namespace this_thread_pthread {
void yield();
ThreadPthread::id get_id();
} // namespace this_thread_pthread
} // namespace detail

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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());

Some files were not shown because too many files have changed in this diff Show More