diff --git a/CMake/GeneratePkgConfig.cmake b/CMake/GeneratePkgConfig.cmake index b9154de13..afbe06ac6 100644 --- a/CMake/GeneratePkgConfig.cmake +++ b/CMake/GeneratePkgConfig.cmake @@ -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") diff --git a/CMake/GetGitRevisionDescription.cmake b/CMake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..a99610c43 --- /dev/null +++ b/CMake/GetGitRevisionDescription.cmake @@ -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( ) +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# 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() diff --git a/CMake/GetGitRevisionDescription.cmake.in b/CMake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..bfc1e54c1 --- /dev/null +++ b/CMake/GetGitRevisionDescription.cmake.in @@ -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 +# 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() diff --git a/CMakeLists.txt b/CMakeLists.txt index 92b8c2fc5..bac718671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/README.md b/README.md index 60a925e5a..c8fd7a77a 100644 --- a/README.md +++ b/README.md @@ -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). ## Using in Java projects diff --git a/benchmark/bench_actor.cpp b/benchmark/bench_actor.cpp index f68a8d8e0..b3a04f592 100644 --- a/benchmark/bench_actor.cpp +++ b/benchmark/bench_actor.cpp @@ -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) { diff --git a/benchmark/bench_queue.cpp b/benchmark/bench_queue.cpp index 0d4911729..6f7cf20bc 100644 --- a/benchmark/bench_queue.cpp +++ b/benchmark/bench_queue.cpp @@ -51,7 +51,7 @@ class Backoff { if (cnt < 50) { return true; } else { - td::this_thread::yield(); + td::usleep_for(1); return cnt < 500; } } diff --git a/build.html b/build.html index 614b93356..10b489f5e 100644 --- a/build.html +++ b/build.html @@ -246,6 +246,7 @@ +

@@ -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 { diff --git a/example/README.md b/example/README.md index 302e0104c..dda7f0a35 100644 --- a/example/README.md +++ b/example/README.md @@ -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. ## 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. - ## 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. ## 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. ## 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. @@ -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. ## 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 ## 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. ## 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. ## 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. ## 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). diff --git a/example/java/CMakeLists.txt b/example/java/CMakeLists.txt index 2c685a774..576eee2cf 100644 --- a/example/java/CMakeLists.txt +++ b/example/java/CMakeLists.txt @@ -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 ) diff --git a/example/java/org/drinkless/tdlib/Client.java b/example/java/org/drinkless/tdlib/Client.java index 3ff95f46c..e9cf5e180 100644 --- a/example/java/org/drinkless/tdlib/Client.java +++ b/example/java/org/drinkless/tdlib/Client.java @@ -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); } diff --git a/example/java/org/drinkless/tdlib/Log.java b/example/java/org/drinkless/tdlib/Log.java deleted file mode 100644 index 1a7f637b1..000000000 --- a/example/java/org/drinkless/tdlib/Log.java +++ /dev/null @@ -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(); - } - } - } -} diff --git a/example/java/org/drinkless/tdlib/example/Example.java b/example/java/org/drinkless/tdlib/example/Example.java index f657c3c8b..486470f6e 100644 --- a/example/java/org/drinkless/tdlib/example/Example.java +++ b/example/java/org/drinkless/tdlib/example/Example.java @@ -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(); + } + } + } } diff --git a/example/java/td_jni.cpp b/example/java/td_jni.cpp index 2621e534c..15c60a954 100644 --- a/example/java/td_jni.cpp +++ b/example/java/td_jni.cpp @@ -5,7 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include -#include #include #include @@ -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(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(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(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; } diff --git a/gen_git_commit_h.ps1 b/gen_git_commit_h.ps1 index 2893f89c2..dadc225cd 100644 --- a/gen_git_commit_h.ps1 +++ b/gen_git_commit_h.ps1 @@ -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 diff --git a/gen_git_commit_h.sh b/gen_git_commit_h.sh index 15ede4cd7..bc6bc2d35 100755 --- a/gen_git_commit_h.sh +++ b/gen_git_commit_h.sh @@ -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 diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 51139efad..6ce691020 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -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 = 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; diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 61e68c5f9..5d87305f6 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -26,6 +26,7 @@ inputStickerSetThumbLegacy#dbaeae9 stickerset:InputStickerSet volume_id:long loc test.useError = Error; test.useConfigSimple = help.ConfigSimple; +test.parseInputAppEvent = InputAppEvent; ---types--- diff --git a/td/mtproto/AuthData.cpp b/td/mtproto/AuthData.cpp index d37f7a9c1..c87ba6ee8 100644 --- a/td/mtproto/AuthData.cpp +++ b/td/mtproto/AuthData.cpp @@ -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; diff --git a/td/telegram/AnimationsManager.cpp b/td/telegram/AnimationsManager.cpp index c6ba1a8e2..feb54d0b2 100644 --- a/td/telegram/AnimationsManager.cpp +++ b/td/telegram/AnimationsManager.cpp @@ -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(); } diff --git a/td/telegram/AnimationsManager.h b/td/telegram/AnimationsManager.h index acddfd5d8..aeec8c56f 100644 --- a/td/telegram/AnimationsManager.h +++ b/td/telegram/AnimationsManager.h @@ -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 &output); diff --git a/td/telegram/Application.cpp b/td/telegram/Application.cpp index 8f4e559d4..ca0ac61a2 100644 --- a/td/telegram/Application.cpp +++ b/td/telegram/Application.cpp @@ -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 &&promise) : promise_(std::move(promise)) { } - void send(const string &type, DialogId dialog_id, tl_object_ptr &&data) { - CHECK(data != nullptr); + void send(telegram_api::object_ptr &&input_app_event) { vector> input_app_events; - input_app_events.push_back( - make_tl_object(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 &&promise) { td->create_handler(std::move(promise))->send(); } +class SaveAppLogLogEvent { + public: + const telegram_api::inputAppEvent *input_app_event_in_ = nullptr; + telegram_api::object_ptr input_app_event_out_; + + template + void store(StorerT &storer) const { + input_app_event_in_->store(storer); + } + + template + void parse(ParserT &parser) { + auto buffer = parser.template fetch_string_raw(parser.get_left_len()); + TlBufferParser buffer_parser{&buffer}; + input_app_event_out_ = telegram_api::make_object(buffer_parser); + } +}; + +static void save_app_log_impl(Td *td, telegram_api::object_ptr input_app_event, + uint64 log_event_id, Promise &&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(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 &&data, Promise &&promise) { - td->create_handler(std::move(promise))->send(type, dialog_id, std::move(data)); + CHECK(data != nullptr); + auto input_app_event = telegram_api::make_object(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()); } } // namespace td diff --git a/td/telegram/Application.h b/td/telegram/Application.h index 4ca4ca7ac..e6d792689 100644 --- a/td/telegram/Application.h +++ b/td/telegram/Application.h @@ -14,6 +14,8 @@ namespace td { +struct BinlogEvent; + class Td; void get_invite_text(Td *td, Promise &&promise); @@ -21,4 +23,6 @@ void get_invite_text(Td *td, Promise &&promise); void save_app_log(Td *td, const string &type, DialogId dialog_id, tl_object_ptr &&data, Promise &&promise); +void on_save_app_log_binlog_event(Td *td, BinlogEvent &&event); + } // namespace td diff --git a/td/telegram/AudiosManager.cpp b/td/telegram/AudiosManager.cpp index 73b6d8a56..4f568baaf 100644 --- a/td/telegram/AudiosManager.cpp +++ b/td/telegram/AudiosManager.cpp @@ -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()) { diff --git a/td/telegram/AudiosManager.h b/td/telegram/AudiosManager.h index b0232f68d..4788f2679 100644 --- a/td/telegram/AudiosManager.h +++ b/td/telegram/AudiosManager.h @@ -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 &output); diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index b6d9fea60..101f54c23 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -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", 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, diff --git a/td/telegram/Client.h b/td/telegram/Client.h index b8c1e814f..01dc8ac02 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -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. diff --git a/td/telegram/ClientDotNet.cpp b/td/telegram/ClientDotNet.cpp index 8d4709e18..44195978e 100644 --- a/td/telegram/ClientDotNet.cpp +++ b/td/telegram/ClientDotNet.cpp @@ -25,10 +25,10 @@ using namespace CxCli; /// /// A type of callback function that will be called when a message is added to the internal TDLib log. /// -/// Log verbosity level with which the message was added (-1 - 1024). +/// 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. -/// Null-terminated string with the message added to the log. +/// The message added to the log. public delegate void LogMessageCallback(int verbosityLevel, String^ message); #endif diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 21350d7fa..ca8ea5f9d 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3398,7 +3398,45 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent channel_participant_cache_timeout_.set_callback_data(static_cast(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> 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 &&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 &&promise) { @@ -8250,7 +8288,7 @@ void ContactsManager::on_import_contacts_finished(int64 random_id, vector 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 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( diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 8a6fb5445..916a2b785 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -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 DocumentsManager::get_document_object(FileId file_id, PhotoFormat thumbnail_format) const { if (!file_id.is_valid()) { diff --git a/td/telegram/DocumentsManager.h b/td/telegram/DocumentsManager.h index e910da4a2..0d660a6cf 100644 --- a/td/telegram/DocumentsManager.h +++ b/td/telegram/DocumentsManager.h @@ -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: diff --git a/td/telegram/DownloadManager.cpp b/td/telegram/DownloadManager.cpp index 9912610e0..202a0272a 100644 --- a/td/telegram/DownloadManager.cpp +++ b/td/telegram/DownloadManager.cpp @@ -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; diff --git a/td/telegram/DownloadManagerCallback.cpp b/td/telegram/DownloadManagerCallback.cpp index 17dd799c5..b41f119c4 100644 --- a/td/telegram/DownloadManagerCallback.cpp +++ b/td/telegram/DownloadManagerCallback.cpp @@ -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 DownloadManagerCallback::get_file_download_object( diff --git a/td/telegram/FileReferenceManager.cpp b/td/telegram/FileReferenceManager.cpp index cb6231d25..1ba11e174 100644 --- a/td/telegram/FileReferenceManager.cpp +++ b/td/telegram/FileReferenceManager.cpp @@ -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_"); } diff --git a/td/telegram/FileReferenceManager.h b/td/telegram/FileReferenceManager.h index a6a8c1fac..ed52da31f 100644 --- a/td/telegram/FileReferenceManager.h +++ b/td/telegram/FileReferenceManager.h @@ -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); diff --git a/tdutils/td/utils/GitInfo.cpp b/td/telegram/GitCommitHash.cpp.in similarity index 65% rename from tdutils/td/utils/GitInfo.cpp rename to td/telegram/GitCommitHash.cpp.in index 9adc64746..89706f132 100644 --- a/tdutils/td/utils/GitInfo.cpp +++ b/td/telegram/GitCommitHash.cpp.in @@ -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 diff --git a/tdutils/td/utils/GitInfo.h b/td/telegram/GitCommitHash.h similarity index 73% rename from tdutils/td/utils/GitInfo.h rename to td/telegram/GitCommitHash.h index c160509ce..f4963d2e8 100644 --- a/tdutils/td/utils/GitInfo.h +++ b/td/telegram/GitCommitHash.h @@ -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 diff --git a/td/telegram/LinkManager.cpp b/td/telegram/LinkManager.cpp index 4ec740ea5..eff592aee 100644 --- a/td/telegram/LinkManager.cpp +++ b/td/telegram/LinkManager.cpp @@ -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::parse_internal_link(Slice link, bool is_trusted) { auto info = get_link_info(link); if (!info.is_internal_) { diff --git a/td/telegram/LinkManager.h b/td/telegram/LinkManager.h index 7ba71ec0b..6c4be78c5 100644 --- a/td/telegram/LinkManager.h +++ b/td/telegram/LinkManager.h @@ -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 parse_internal_link(Slice link, bool is_trusted = false); diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 73ce9e572..2938372bf 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -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; diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index d4c205c9c..c3f947590 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -1636,7 +1636,7 @@ static void fix_entity_offsets(Slice text, vector &entities) { } } -vector find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps, bool skip_urls) { +vector find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) { vector entities; auto add_entities = [&entities, &text](MessageEntity::Type type, vector (*find_entities_f)(Slice)) mutable { @@ -1655,15 +1655,13 @@ vector 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(url.first.begin() - text.begin()); - auto length = narrow_cast(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(url.first.begin() - text.begin()); + auto length = narrow_cast(url.first.size()); + entities.emplace_back(type, offset, length); } if (!skip_media_timestamps) { auto media_timestamps = find_media_timestamps(text); diff --git a/td/telegram/MessageEntity.h b/td/telegram/MessageEntity.h index 88600c26a..4a0ec6e29 100644 --- a/td/telegram/MessageEntity.h +++ b/td/telegram/MessageEntity.h @@ -143,8 +143,7 @@ vector> get_text_entities_object(const vector< td_api::object_ptr get_formatted_text_object(const FormattedText &text, bool skip_bot_commands, int32 max_media_timestamp); -vector find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps, - bool skip_urls = false); +vector find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps); vector find_mentions(Slice str); vector find_bot_commands(Slice str); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index de074e8e7..d5532ffc4 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -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(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 &&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 &&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 &&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; } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index e8a8cb12c..58e3a3695 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -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 void store(StorerT &storer) const; diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index 7e0ac28e0..acf3e701c 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -984,6 +984,17 @@ void NotificationManager::add_update_notification_group(td_api::object_ptrnotification_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 ¬ification) { + CHECK(notification != nullptr); + if (td::contains(update->removed_notification_ids_, notification->id_)) { + LOG(ERROR) << "Have the same notification as added and removed"; + return true; + } + return false; + }); + } add_update(group_id, std::move(update)); } diff --git a/td/telegram/OptionManager.cpp b/td/telegram/OptionManager.cpp index ba903b603..e1fb5aff2 100644 --- a/td/telegram/OptionManager.cpp +++ b/td/telegram/OptionManager.cpp @@ -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 &OptionManager::get_synchronous_options() { + static const vector 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 OptionManager::get_option_synchronously(Slice name) { + CHECK(!name.empty()); + switch (name[0]) { + case 'c': + if (name == "commit_hash") { + return td_api::make_object(get_git_commit_hash()); + } + break; case 'v': if (name == "version") { - return promise.set_value(td_api::make_object(Td::TDLIB_VERSION)); + return td_api::make_object("1.8.4"); } break; } - wrap_promise().set_value(Unit()); + UNREACHABLE(); } void OptionManager::set_option(const string &name, td_api::object_ptr &&value, @@ -792,9 +815,15 @@ td_api::object_ptr OptionManager::get_option_value_object(S return td_api::make_object(value.str()); } +void OptionManager::get_common_state(vector> &updates) { + for (auto option_name : get_synchronous_options()) { + updates.push_back( + td_api::make_object(option_name.str(), get_option_synchronously(option_name))); + } +} + void OptionManager::get_current_state(vector> &updates) const { - updates.push_back(td_api::make_object( - "version", td_api::make_object(Td::TDLIB_VERSION))); + get_common_state(updates); updates.push_back(td_api::make_object( "online", td_api::make_object(td_->is_online()))); diff --git a/td/telegram/OptionManager.h b/td/telegram/OptionManager.h index 2bfd1d341..5b21739d0 100644 --- a/td/telegram/OptionManager.h +++ b/td/telegram/OptionManager.h @@ -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 get_option_synchronously(Slice name); + + static void get_common_state(vector> &updates); + void get_current_state(vector> &updates) const; private: @@ -45,6 +51,8 @@ class OptionManager final : public Actor { static bool is_internal_option(Slice name); + static const vector &get_synchronous_options(); + static td_api::object_ptr get_unix_time_option_value_object(); static td_api::object_ptr get_option_value_object(Slice value); diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index ecf363a70..f28b541b1 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -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()) { diff --git a/td/telegram/SecretChatsManager.h b/td/telegram/SecretChatsManager.h index cacd5263c..554456ebc 100644 --- a/td/telegram/SecretChatsManager.h +++ b/td/telegram/SecretChatsManager.h @@ -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 update); void on_new_message(tl_object_ptr &&message_ptr, Promise &&promise); diff --git a/td/telegram/StickerFormat.cpp b/td/telegram/StickerFormat.cpp index ddf05a5fa..88e8f573f 100644 --- a/td/telegram/StickerFormat.cpp +++ b/td/telegram/StickerFormat.cpp @@ -10,6 +10,23 @@ namespace td { +StickerFormat get_sticker_format(const td_api::object_ptr &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; diff --git a/td/telegram/StickerFormat.h b/td/telegram/StickerFormat.h index 6e1d537ee..b367a6cab 100644 --- a/td/telegram/StickerFormat.h +++ b/td/telegram/StickerFormat.h @@ -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 &type); + StickerFormat get_sticker_format_by_mime_type(Slice mime_type); StickerFormat get_sticker_format_by_extension(Slice extension); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 9a4040ed7..a42542441 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -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 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 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 StickersManager::get_sticker_set_object(Sticke } emojis.push_back(make_tl_object(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( - 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 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(sticker_set->sticker_ids.size()); return make_tl_object( - 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> 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> StickersManager::prepare_input_file( diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index 8db25ca8f..f7d9ab386 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -52,6 +52,11 @@ class StickersManager final : public Actor { static vector convert_sticker_set_ids(const vector &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 get_mask_point_object(int32 point); + td_api::object_ptr get_sticker_set_thumbnail_object(const StickerSet *sticker_set) const; + tl_object_ptr get_sticker_set_info_object(StickerSetId sticker_set_id, size_t covers_limit, bool prefer_premium) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 3e6a24975..47e05887b 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -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(function)->name_); default: return false; } @@ -3010,6 +3012,14 @@ td_api::object_ptr Td::get_fake_authorization_state_ } } +vector> Td::get_fake_current_state() const { + CHECK(state_ != State::Run); + vector> updates; + OptionManager::get_common_state(updates); + updates.push_back(td_api::make_object(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 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 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> updates; - updates.push_back(td_api::make_object( - "version", td_api::make_object(TDLIB_VERSION))); - updates.push_back(td_api::make_object(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(std::move(updates))); - } + return send_result(id, td_api::make_object(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()); @@ -3280,10 +3285,9 @@ void Td::start_up() { alarm_timeout_.set_callback_data(static_cast(this)); CHECK(state_ == State::WaitParameters); - send_update(td_api::make_object("version", - td_api::make_object(TDLIB_VERSION))); - send_update(td_api::make_object( - td_api::make_object())); + for (auto &update : get_fake_current_state()) { + send_update(std::move(update)); + } } void Td::tear_down() { @@ -3818,6 +3822,10 @@ void Td::init(Result 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 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 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(version.get())->value_; } options_.language_pack = string(); options_.language_code = string(); @@ -8235,6 +8248,13 @@ td_api::object_ptr Td::do_static_request(td_api::parseMarkdown & return get_formatted_text_object(parsed_text, false, std::numeric_limits::max()); } +td_api::object_ptr 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::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 diff --git a/td/telegram/Td.h b/td/telegram/Td.h index fe56c8d3c..5dae66f36 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -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 net_query_stats; }; @@ -339,6 +337,8 @@ class Td final : public Actor { td_api::object_ptr get_fake_authorization_state_object() const; + vector> 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 do_static_request(const T &request) { return td_api::make_object(400, "The method can't be executed synchronously"); } + static td_api::object_ptr do_static_request(const td_api::getOption &request); static td_api::object_ptr do_static_request(const td_api::getTextEntities &request); static td_api::object_ptr do_static_request(td_api::parseTextEntities &request); static td_api::object_ptr do_static_request(td_api::parseMarkdown &request); diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index 96e177136..8c0dd20ea 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -117,6 +117,9 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &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; diff --git a/td/telegram/TdDb.h b/td/telegram/TdDb.h index 14beacfa3..f38fb7c42 100644 --- a/td/telegram/TdDb.h +++ b/td/telegram/TdDb.h @@ -63,6 +63,7 @@ class TdDb { vector channel_events; vector secret_chat_events; vector web_page_events; + vector save_app_log_events; vector to_poll_manager; vector to_messages_manager; vector to_notification_manager; diff --git a/td/telegram/VideoNotesManager.cpp b/td/telegram/VideoNotesManager.cpp index 825de398c..94337e260 100644 --- a/td/telegram/VideoNotesManager.cpp +++ b/td/telegram/VideoNotesManager.cpp @@ -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()); diff --git a/td/telegram/VideoNotesManager.h b/td/telegram/VideoNotesManager.h index e26201bca..cb33d155f 100644 --- a/td/telegram/VideoNotesManager.h +++ b/td/telegram/VideoNotesManager.h @@ -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 &output); diff --git a/td/telegram/VideosManager.cpp b/td/telegram/VideosManager.cpp index ffd40b295..98e9eae9c 100644 --- a/td/telegram/VideosManager.cpp +++ b/td/telegram/VideosManager.cpp @@ -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()); diff --git a/td/telegram/VideosManager.h b/td/telegram/VideosManager.h index a529e4b37..bc83ba7b3 100644 --- a/td/telegram/VideosManager.h +++ b/td/telegram/VideosManager.h @@ -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 &output); diff --git a/td/telegram/VoiceNotesManager.cpp b/td/telegram/VoiceNotesManager.cpp index bfa8869fe..593e0b3e0 100644 --- a/td/telegram/VoiceNotesManager.cpp +++ b/td/telegram/VoiceNotesManager.cpp @@ -100,6 +100,12 @@ VoiceNotesManager::VoiceNotesManager(Td *td, ActorShared<> parent) : td_(td), pa voice_note_transcription_timeout_.set_callback_data(static_cast(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(); } diff --git a/td/telegram/VoiceNotesManager.h b/td/telegram/VoiceNotesManager.h index 737e8e4cd..722005f57 100644 --- a/td/telegram/VoiceNotesManager.h +++ b/td/telegram/VoiceNotesManager.h @@ -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; diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index 4baa90c82..fb30753c1 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -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 &&web_page_ptr, DialogId owner_dialog_id) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 75dcea83b..ba7ea6a48 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2401,6 +2401,8 @@ class CliClient final : public Actor { td_api::make_object(op == "on"))); } else if (op == "go") { send_request(td_api::make_object(args)); + } else if (op == "gos") { + execute(td_api::make_object(args)); } else if (op == "sob") { string name; bool value; diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index d6fd3fc87..309b57535 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -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"); diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index e99501d41..2d651a544 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -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 callback, int32 new_priority, int64 offset, int64 limit); void upload(FileId file_id, std::shared_ptr callback, int32 new_priority, uint64 upload_order); diff --git a/td/telegram/files/FileManager.hpp b/td/telegram/files/FileManager.hpp index 520739016..9cf26745c 100644 --- a/td/telegram/files/FileManager.hpp +++ b/td/telegram/files/FileManager.hpp @@ -224,9 +224,9 @@ FileId FileManager::parse_file(ParserT &parser) { if (expected_size < 0) { expected_size += static_cast(1) << 32; } + int32 zero; + parse(zero, parser); } - int32 zero; - parse(zero, parser); DialogId owner_dialog_id; if (parser.version() >= static_cast(Version::StoreFileOwnerId)) { parse(owner_dialog_id, parser); diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h index d0f0a80d6..2a4fee9b0 100644 --- a/td/telegram/logevent/LogEvent.h +++ b/td/telegram/logevent/LogEvent.h @@ -104,6 +104,7 @@ class LogEvent { GetChannelDifference = 0x140, AddMessagePushNotification = 0x200, EditMessagePushNotification = 0x201, + SaveAppLog = 0x300, ConfigPmcMagic = 0x1f18, BinlogPmcMagic = 0x4327 }; diff --git a/td/telegram/net/NetQueryDispatcher.cpp b/td/telegram/net/NetQueryDispatcher.cpp index ccbe5a642..e94845e72 100644 --- a/td/telegram/net/NetQueryDispatcher.cpp +++ b/td/telegram/net/NetQueryDispatcher.cpp @@ -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 guard(main_dc_id_mutex_); if (new_main_dc_id == main_dc_id_) { return; diff --git a/td/telegram/net/Session.cpp b/td/telegram/net/Session.cpp index 9693bfb19..c04ce0f22 100644 --- a/td/telegram/net/Session.cpp +++ b/td/telegram/net/Session.cpp @@ -69,11 +69,6 @@ class SemaphoreActor final : public Actor { size_t capacity_; VectorQueue>> pending_; - void start_up() final { - set_context(std::make_shared()); - set_tag(string()); - } - void finish(Result) { 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()); + auto old_tag = set_tag(string()); init_thread_local(semaphore_, 50); + set_context(std::move(old_context)); + set_tag(std::move(old_tag)); return *semaphore_; } diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index e6e0c2ded..a12fa01b6 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -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. diff --git a/tdactor/td/actor/Timeout.h b/tdactor/td/actor/Timeout.h index 5c2293b99..cde72657b 100644 --- a/tdactor/td/actor/Timeout.h +++ b/tdactor/td/actor/Timeout.h @@ -8,6 +8,8 @@ #include "td/actor/actor.h" +#include "td/utils/common.h" + namespace td { class Timeout final : public Actor { diff --git a/tdactor/td/actor/impl/Event.h b/tdactor/td/actor/impl/Event.h index 32270e09a..2796c701e 100644 --- a/tdactor/td/actor/impl/Event.h +++ b/tdactor/td/actor/impl/Event.h @@ -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 @@ -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(actor)); } - CustomEvent *clone() const final { - return new ClosureEvent(closure_.clone()); - } template explicit ClosureEvent(ArgsT &&...args) : closure_(std::forward(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 , LambdaEvent>::value, int> = 0> explicit LambdaEvent(FromLambdaT &&lambda) : f_(std::forward(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; } diff --git a/tdactor/td/actor/impl/Scheduler-decl.h b/tdactor/td/actor/impl/Scheduler-decl.h index 396995e6c..57f49dbb4 100644 --- a/tdactor/td/actor/impl/Scheduler-decl.h +++ b/tdactor/td/actor/impl/Scheduler-decl.h @@ -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 action); // TODO Action + template + void destroy_on_scheduler(int32 sched_id, T &value); + template 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 action); + class ServiceActor final : public Actor { public: void set_queue(std::shared_ptr> queues); diff --git a/tdactor/td/actor/impl/Scheduler.cpp b/tdactor/td/actor/impl/Scheduler.cpp index 9ad7cb72c..9edcaff40 100644 --- a/tdactor/td/actor/impl/Scheduler.cpp +++ b/tdactor/td/actor/impl/Scheduler.cpp @@ -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 action) { action.set_value(Unit()); } +void Scheduler::destroy_on_scheduler_impl(int32 sched_id, Promise action) { + auto empty_context = std::make_shared(); + 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(); diff --git a/tdactor/td/actor/impl/Scheduler.h b/tdactor/td/actor/impl/Scheduler.h index 3cdae11fe..182066145 100644 --- a/tdactor/td/actor/impl/Scheduler.h +++ b/tdactor/td/actor/impl/Scheduler.h @@ -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_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 +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 } diff --git a/tdnet/td/net/SslStream.cpp b/tdnet/td/net/SslStream.cpp index 8cdf88b9c..2c88632f3 100644 --- a/tdnet/td/net/SslStream.cpp +++ b/tdnet/td/net/SslStream.cpp @@ -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; } diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 12d092654..1697390d6 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -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}) diff --git a/tdutils/generate/CMakeLists.txt b/tdutils/generate/CMakeLists.txt index 82f544934..69b42d914 100644 --- a/tdutils/generate/CMakeLists.txt +++ b/tdutils/generate/CMakeLists.txt @@ -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=\"\"'.") + 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=\"\"'.") endif() set(GPERF_FILES diff --git a/tdutils/td/utils/AtomicRead.h b/tdutils/td/utils/AtomicRead.h index a51a91549..d30e960a8 100644 --- a/tdutils/td/utils/AtomicRead.h +++ b/tdutils/td/utils/AtomicRead.h @@ -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 @@ -20,6 +20,15 @@ template 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(); } } diff --git a/tdutils/td/utils/Closure.h b/tdutils/td/utils/Closure.h index 999c9de1a..345f1b1f8 100644 --- a/tdutils/td/utils/Closure.h +++ b/tdutils/td/utils/Closure.h @@ -8,9 +8,7 @@ #include "td/utils/common.h" #include "td/utils/invoke.h" -#include "td/utils/logging.h" -#include #include #include #include @@ -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 @@ -92,11 +89,6 @@ template class DelayedClosure { public: using ActorType = ActorT; - using Delayed = DelayedClosure; - - DelayedClosure clone() const { - return do_clone(*this); - } explicit DelayedClosure(ImmediateClosure &&other) : args(std::move(other.args)) { } @@ -110,38 +102,7 @@ class DelayedClosure { } private: - using ArgsStorageT = std::tuple::type...>; - - ArgsStorageT args; - - template - explicit DelayedClosure(const DelayedClosure &other, - std::enable_if_t::value...>::value, int> = 0) - : args(other.args) { - } - - template - explicit DelayedClosure( - const DelayedClosure &other, - std::enable_if_t::value...>::value, int> = 0) { - LOG(FATAL) << "Deleted constructor"; - std::abort(); - } - - template - std::enable_if_t::value...>::value, - DelayedClosure> - do_clone(const DelayedClosure &value) const { - LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements"; - std::abort(); - } - - template - std::enable_if_t::value...>::value, - DelayedClosure> - do_clone(const DelayedClosure &value) const { - return DelayedClosure(value); - } + std::tuple::type...> args; public: auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) { @@ -149,16 +110,6 @@ class DelayedClosure { } }; -template -typename ImmediateClosure::Delayed to_delayed_closure(ImmediateClosure &&other) { - return typename ImmediateClosure::Delayed(std::move(other)); -} - -template -DelayedClosure to_delayed_closure(DelayedClosure &&other) { - return std::move(other); -} - template auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&...args) { return DelayedClosure(func, diff --git a/tdutils/td/utils/Enumerator.h b/tdutils/td/utils/Enumerator.h index e89fd6718..1b073ca5d 100644 --- a/tdutils/td/utils/Enumerator.h +++ b/tdutils/td/utils/Enumerator.h @@ -42,6 +42,10 @@ class Enumerator { return arr_.size(); } + bool empty() const { + return size() == 0; + } + private: std::map map_; std::vector arr_; diff --git a/tdutils/td/utils/MpmcQueue.h b/tdutils/td/utils/MpmcQueue.h index 526bc00be..f1f12a37f 100644 --- a/tdutils/td/utils/MpmcQueue.h +++ b/tdutils/td/utils/MpmcQueue.h @@ -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 @@ -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); } } diff --git a/tdutils/td/utils/MpmcWaiter.h b/tdutils/td/utils/MpmcWaiter.h index b6e0bee0c..7ece498ff 100644 --- a/tdutils/td/utils/MpmcWaiter.h +++ b/tdutils/td/utils/MpmcWaiter.h @@ -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 #include @@ -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; } diff --git a/tdutils/td/utils/MpscPollableQueue.h b/tdutils/td/utils/MpscPollableQueue.h index e4c7ba2b2..4daf4a0b9 100644 --- a/tdutils/td/utils/MpscPollableQueue.h +++ b/tdutils/td/utils/MpscPollableQueue.h @@ -12,7 +12,7 @@ #if !TD_EVENTFD_UNSUPPORTED -#include "td/utils/SpinLock.h" +#include "td/utils/port/Mutex.h" #include @@ -91,7 +91,7 @@ class MpscPollableQueue { } private: - SpinLock lock_; + Mutex lock_; bool wait_event_fd_{false}; EventFd event_fd_; std::vector writer_vector_; diff --git a/tdutils/td/utils/SpinLock.h b/tdutils/td/utils/SpinLock.h index 33230b8db..3413f6c5b 100644 --- a/tdutils/td/utils/SpinLock.h +++ b/tdutils/td/utils/SpinLock.h @@ -6,7 +6,7 @@ // #pragma once -#include "td/utils/port/thread.h" +#include "td/utils/port/sleep.h" #include #include @@ -30,7 +30,7 @@ class SpinLock { //TODO pause return true; } else { - td::this_thread::yield(); + usleep_for(1); return true; } } diff --git a/tdutils/td/utils/WaitFreeHashMap.h b/tdutils/td/utils/WaitFreeHashMap.h index 0d32e0bd5..14f6af8d3 100644 --- a/tdutils/td/utils/WaitFreeHashMap.h +++ b/tdutils/td/utils/WaitFreeHashMap.h @@ -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 diff --git a/tdutils/td/utils/port/Mutex.h b/tdutils/td/utils/port/Mutex.h new file mode 100644 index 000000000..40d9f482d --- /dev/null +++ b/tdutils/td/utils/port/Mutex.h @@ -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 + +namespace td { + +class Mutex { + public: + struct Guard { + std::unique_lock guard; + void reset() { + guard.unlock(); + } + }; + + Guard lock() { + return {std::unique_lock(mutex_)}; + } + + private: + std::mutex mutex_; +}; + +} // namespace td diff --git a/tdutils/td/utils/port/ServerSocketFd.cpp b/tdutils/td/utils/port/ServerSocketFd.cpp index 6e4158c1c..139252b40 100644 --- a/tdutils/td/utils/port/ServerSocketFd.cpp +++ b/tdutils/td/utils/port/ServerSocketFd.cpp @@ -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 accepted_; VectorQueue pending_errors_; static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16; diff --git a/tdutils/td/utils/port/SocketFd.cpp b/tdutils/td/utils/port/SocketFd.cpp index 9076c4e4a..90d516db3 100644 --- a/tdutils/td/utils/port/SocketFd.cpp +++ b/tdutils/td/utils/port/SocketFd.cpp @@ -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 @@ -164,7 +164,7 @@ class SocketFdImpl final : private Iocp::Callback { private: PollableFdInfo info_; - SpinLock lock_; + Mutex lock_; std::atomic refcnt_{1}; bool close_flag_{false}; diff --git a/tdutils/td/utils/port/UdpSocketFd.cpp b/tdutils/td/utils/port/UdpSocketFd.cpp index d33d24340..eee487d44 100644 --- a/tdutils/td/utils/port/UdpSocketFd.cpp +++ b/tdutils/td/utils/port/UdpSocketFd.cpp @@ -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 refcnt_{1}; bool is_connected_{false}; diff --git a/tdutils/td/utils/port/detail/PollableFd.h b/tdutils/td/utils/port/detail/PollableFd.h index ba8c8891b..01fb5857e 100644 --- a/tdutils/td/utils/port/detail/PollableFd.h +++ b/tdutils/td/utils/port/detail/PollableFd.h @@ -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 #include @@ -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}; diff --git a/tdutils/td/utils/port/detail/ThreadPthread.cpp b/tdutils/td/utils/port/detail/ThreadPthread.cpp index a342c3ad1..30234d0aa 100644 --- a/tdutils/td/utils/port/detail/ThreadPthread.cpp +++ b/tdutils/td/utils/port/detail/ThreadPthread.cpp @@ -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(); } diff --git a/tdutils/td/utils/port/detail/ThreadPthread.h b/tdutils/td/utils/port/detail/ThreadPthread.h index 5342c76c0..c50546c14 100644 --- a/tdutils/td/utils/port/detail/ThreadPthread.h +++ b/tdutils/td/utils/port/detail/ThreadPthread.h @@ -83,7 +83,6 @@ class ThreadPthread { }; namespace this_thread_pthread { -void yield(); ThreadPthread::id get_id(); } // namespace this_thread_pthread } // namespace detail diff --git a/tdutils/td/utils/port/detail/ThreadStl.h b/tdutils/td/utils/port/detail/ThreadStl.h index 5c1a61205..0d9f8f2ff 100644 --- a/tdutils/td/utils/port/detail/ThreadStl.h +++ b/tdutils/td/utils/port/detail/ThreadStl.h @@ -70,7 +70,9 @@ class ThreadStl { return std::forward(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 diff --git a/tdutils/td/utils/queue.h b/tdutils/td/utils/queue.h index 6157cc808..e6d69aae3 100644 --- a/tdutils/td/utils/queue.h +++ b/tdutils/td/utils/queue.h @@ -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; } } diff --git a/tdutils/td/utils/tests.h b/tdutils/td/utils/tests.h index 183cfe640..69b920626 100644 --- a/tdutils/td/utils/tests.h +++ b/tdutils/td/utils/tests.h @@ -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 +#include #include +#include #include 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 value_{0}; }; +class StageMutex { + public: + void wait(uint64 need) { + std::unique_lock 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 rand_split(Slice str); diff --git a/tdutils/test/ConcurrentHashMap.cpp b/tdutils/test/ConcurrentHashMap.cpp index 3e2219f1a..afdb8aea8 100644 --- a/tdutils/test/ConcurrentHashMap.cpp +++ b/tdutils/test/ConcurrentHashMap.cpp @@ -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 -#include #if !TD_THREAD_UNSUPPORTED @@ -72,11 +72,11 @@ class ConcurrentHashMapMutex { return "ConcurrentHashMapMutex"; } void insert(KeyT key, ValueT value) { - std::unique_lock lock(mutex_); + auto guard = mutex_.lock(); hash_map_.emplace(key, value); } ValueT find(KeyT key, ValueT default_value) { - std::unique_lock 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 hash_map_; #else diff --git a/tdutils/test/HashSet.cpp b/tdutils/test/HashSet.cpp index 83831bd6f..507960c4b 100644 --- a/tdutils/test/HashSet.cpp +++ b/tdutils/test/HashSet.cpp @@ -265,8 +265,7 @@ TEST(FlatHashMap, stress_test) { td::vector 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()); diff --git a/tdutils/test/WaitFreeHashMap.cpp b/tdutils/test/WaitFreeHashMap.cpp index 8865d5ffd..8c1b0c1db 100644 --- a/tdutils/test/WaitFreeHashMap.cpp +++ b/tdutils/test/WaitFreeHashMap.cpp @@ -30,6 +30,8 @@ TEST(WaitFreeHashMap, stress_test) { reference[key] = value; map.set(key, value); ASSERT_EQ(reference[key], map.get(key)); + ASSERT_EQ(reference.size(), map.size()); + ASSERT_EQ(reference.empty(), map.empty()); }); add_step(2000, [&] { @@ -37,6 +39,8 @@ TEST(WaitFreeHashMap, stress_test) { auto ref_it = reference.find(key); auto ref_value = ref_it == reference.end() ? 0 : ref_it->second; ASSERT_EQ(ref_value, map.get(key)); + ASSERT_EQ(reference.size(), map.size()); + ASSERT_EQ(reference.empty(), map.empty()); }); add_step(500, [&] { @@ -44,6 +48,8 @@ TEST(WaitFreeHashMap, stress_test) { size_t reference_erased_count = reference.erase(key); size_t map_erased_count = map.erase(key); ASSERT_EQ(reference_erased_count, map_erased_count); + ASSERT_EQ(reference.size(), map.size()); + ASSERT_EQ(reference.empty(), map.empty()); }); td::RandomSteps runner(std::move(steps)); diff --git a/test/link.cpp b/test/link.cpp index dd5ebb17f..ace300ff5 100644 --- a/test/link.cpp +++ b/test/link.cpp @@ -95,6 +95,14 @@ static void parse_internal_link(const td::string &url, td::td_api::object_ptr( + can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, can_manage_video_chats, is_anonymous); + }; auto target_chat_chosen = [](bool allow_users, bool allow_bots, bool allow_groups, bool allow_channels) { return td::td_api::make_object(allow_users, allow_bots, allow_groups, allow_channels); }; @@ -734,29 +742,27 @@ TEST(Link, parse_internal_link) { parse_internal_link("tg:resolve?domain=username&startgroup&admin=asdas", bot_start_in_group("username", "", nullptr)); parse_internal_link("tg:resolve?domain=username&startgroup&admin=post_messages", bot_start_in_group("username", "", nullptr)); - parse_internal_link( - "tg:resolve?domain=username&startgroup=1&admin=delete_messages+anonymous", - bot_start_in_group("username", "1", - td::td_api::make_object( - true, false, false, false, true, false, false, false, false, false, true))); + parse_internal_link("tg:resolve?domain=username&startgroup=1&admin=delete_messages+anonymous", + bot_start_in_group("username", "1", + chat_administrator_rights(true, false, false, false, true, false, false, false, + false, false, true))); parse_internal_link( "tg:resolve?domain=username&startgroup&admin=manage_chat+change_info+post_messages+edit_messages+delete_messages+" "invite_users+restrict_members+pin_messages+promote_members+manage_video_chats+anonymous", - bot_start_in_group("username", "", - td::td_api::make_object( - true, true, false, false, true, true, true, true, true, true, true))); + bot_start_in_group( + "username", "", + chat_administrator_rights(true, true, false, false, true, true, true, true, true, true, true))); parse_internal_link("tg:resolve?domain=username&startchannel", public_chat("username")); parse_internal_link("tg:resolve?domain=username&startchannel&admin=", public_chat("username")); - parse_internal_link( - "tg:resolve?domain=username&startchannel&admin=post_messages", - bot_add_to_channel("username", td::td_api::make_object( - true, false, true, false, false, false, true, false, false, false, false))); + parse_internal_link("tg:resolve?domain=username&startchannel&admin=post_messages", + bot_add_to_channel("username", chat_administrator_rights(true, false, true, false, false, false, + true, false, false, false, false))); parse_internal_link( "tg:resolve?domain=username&startchannel&admin=manage_chat+change_info+post_messages+edit_messages+delete_" "messages+invite_users+restrict_members+pin_messages+promote_members+manage_video_chats+anonymous", - bot_add_to_channel("username", td::td_api::make_object( - true, true, true, true, true, true, true, false, true, true, false))); + bot_add_to_channel( + "username", chat_administrator_rights(true, true, true, true, true, true, true, false, true, true, false))); parse_internal_link("t.me/username/0/a//s/as?startgroup=", bot_start_in_group("username", "", nullptr)); parse_internal_link("t.me/username/aasdas?test=1&startgroup=#12312", bot_start_in_group("username", "", nullptr)); @@ -771,31 +777,29 @@ TEST(Link, parse_internal_link) { parse_internal_link("t.me/username?startgroup", bot_start_in_group("username", "", nullptr)); parse_internal_link("t.me/username?startgroup&admin=asdas", bot_start_in_group("username", "", nullptr)); parse_internal_link("t.me/username?startgroup&admin=post_messages", bot_start_in_group("username", "", nullptr)); - parse_internal_link( - "t.me/username?startgroup=1&admin=delete_messages+anonymous", - bot_start_in_group("username", "1", - td::td_api::make_object( - true, false, false, false, true, false, false, false, false, false, true))); + parse_internal_link("t.me/username?startgroup=1&admin=delete_messages+anonymous", + bot_start_in_group("username", "1", + chat_administrator_rights(true, false, false, false, true, false, false, false, + false, false, true))); parse_internal_link( "t.me/" "username?startgroup&admin=manage_chat+change_info+post_messages+edit_messages+delete_messages+invite_users+" "restrict_members+pin_messages+promote_members+manage_video_chats+anonymous", - bot_start_in_group("username", "", - td::td_api::make_object( - true, true, false, false, true, true, true, true, true, true, true))); + bot_start_in_group( + "username", "", + chat_administrator_rights(true, true, false, false, true, true, true, true, true, true, true))); parse_internal_link("t.me/username?startchannel", public_chat("username")); parse_internal_link("t.me/username?startchannel&admin=", public_chat("username")); - parse_internal_link( - "t.me/username?startchannel&admin=post_messages", - bot_add_to_channel("username", td::td_api::make_object( - true, false, true, false, false, false, true, false, false, false, false))); + parse_internal_link("t.me/username?startchannel&admin=post_messages", + bot_add_to_channel("username", chat_administrator_rights(true, false, true, false, false, false, + true, false, false, false, false))); parse_internal_link( "t.me/" "username?startchannel&admin=manage_chat+change_info+post_messages+edit_messages+delete_messages+invite_users+" "restrict_members+pin_messages+promote_members+manage_video_chats+anonymous", - bot_add_to_channel("username", td::td_api::make_object( - true, true, true, true, true, true, true, false, true, true, false))); + bot_add_to_channel( + "username", chat_administrator_rights(true, true, true, true, true, true, true, false, true, true, false))); parse_internal_link("tg:resolve?domain=username&game=aasdasd", game("username", "aasdasd")); parse_internal_link("TG://resolve?domain=username&game=", public_chat("username"));