From ccc91690515cd107bddbd0e26650aa337452606f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 5 Oct 2020 00:19:49 +0300 Subject: [PATCH 1/4] Load all service accounts in getUser. GitOrigin-RevId: 6e36be3e2e9e8decae1cc599b714ff218b892841 --- td/telegram/ContactsManager.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 04449d3fd..f3e6f2cd4 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -7552,6 +7552,7 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { int32 profile_photo_local_id = 0; int32 profile_photo_dc_id = 1; string first_name; + string last_name; string username; string phone_number; int32 bot_info_version = 0; @@ -7559,6 +7560,10 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { if (user_id == get_service_notifications_user_id()) { flags |= telegram_api::user::PHONE_MASK | telegram_api::user::VERIFIED_MASK | telegram_api::user::SUPPORT_MASK; first_name = "Telegram"; + if (G()->is_test_dc()) { + flags |= telegram_api::user::LAST_NAME_MASK; + last_name = "Notifications"; + } phone_number = "42777"; profile_photo_id = 3337190045231023; profile_photo_volume_id = 107738948; @@ -12338,8 +12343,9 @@ bool ContactsManager::get_user(UserId user_id, int left_tries, Promise &&p return false; } - if (user_id == UserId(777000)) { - get_user_force(user_id); // preload 777000 synchronously + if (user_id == get_service_notifications_user_id() || user_id == get_replies_bot_user_id() || + user_id == get_anonymous_bot_user_id()) { + get_user_force(user_id); } // TODO support loading user from database and merging it with min-user in memory From 77fff9c578bb8340ec44c8d193c6419e31713671 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 5 Oct 2020 00:51:45 +0300 Subject: [PATCH 2/4] Use GroupAnonymousBot as a fallback sender for bots instead of 777000. GitOrigin-RevId: 30e2ff3c9db2efc05b3bcbfd610ad8d6492ad66e --- td/telegram/MessagesManager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index becb6fa96..037dfe1f6 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -12538,7 +12538,11 @@ std::pair> MessagesManager::creat sender_user_id = UserId(); } if (!is_broadcast_channel(dialog_id) && td_->auth_manager_->is_bot()) { - sender_user_id = td_->contacts_manager_->add_service_notifications_user(); + if (dialog_id == sender_dialog_id) { + sender_user_id = td_->contacts_manager_->add_anonymous_bot_user(); + } else { + sender_user_id = td_->contacts_manager_->add_service_notifications_user(); + } } } if (sender_dialog_id.is_valid()) { From b53a28c43e6eea3c6bd022d6751243cdfc77593b Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 5 Oct 2020 01:10:42 +0300 Subject: [PATCH 3/4] Add fb2 MIME type extension support. GitOrigin-RevId: c77d8be1d4b5d84610c8acdbc4b6ce34ca8b6c2f --- tdutils/generate/mime_types.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tdutils/generate/mime_types.txt b/tdutils/generate/mime_types.txt index 4f8c9d324..188845c27 100644 --- a/tdutils/generate/mime_types.txt +++ b/tdutils/generate/mime_types.txt @@ -498,6 +498,7 @@ application/x-dtbresource+xml res application/x-dvi dvi application/x-envoy evy application/x-eva eva +application/x-fictionbook+xml fb2 application/x-font-bdf bdf application/x-font-ghostscript gsf application/x-font-linux-psf psf From 3fc140b4a260c606e5c6d79ef148d4beeb37048a Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 5 Oct 2020 14:32:23 +0300 Subject: [PATCH 4/4] Add ClientManager documentation. GitOrigin-RevId: 5254af6d55288851e633a6567d700f6b15b18d1a --- td/telegram/Client.cpp | 81 ++++++++++++++------------- td/telegram/Client.h | 123 +++++++++++++++++++++++++++++++++++------ test/tdclient.cpp | 6 +- 3 files changed, 150 insertions(+), 60 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index e8e6cbe49..68ae138ec 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -45,10 +45,11 @@ class MultiTd : public Actor { set_tag(old_tag); } - void send(MultiClient::ClientId client_id, MultiClient::RequestId request_id, MultiClient::Function function) { + void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id, + td_api::object_ptr &&request) { auto &td = tds_[client_id]; CHECK(!td.empty()); - send_closure(td, &Td::request, request_id, std::move(function)); + send_closure(td, &Td::request, request_id, std::move(request)); } void close(int32 td_id) { @@ -64,7 +65,7 @@ class MultiTd : public Actor { #if TD_THREAD_UNSUPPORTED || TD_EVENTFD_UNSUPPORTED class TdReceiver { public: - MultiClient::Response receive(double timeout) { + ClientManager::Response receive(double timeout) { if (!responses_.empty()) { auto result = std::move(responses_.front()); responses_.pop(); @@ -73,10 +74,10 @@ class TdReceiver { return {0, 0, nullptr}; } - unique_ptr create_callback(MultiClient::ClientId client_id) { + unique_ptr create_callback(ClientManager::ClientId client_id) { class Callback : public TdCallback { public: - Callback(MultiClient::ClientId client_id, TdReceiver *impl) : client_id_(client_id), impl_(impl) { + Callback(ClientManager::ClientId client_id, TdReceiver *impl) : client_id_(client_id), impl_(impl) { } void on_result(uint64 id, td_api::object_ptr result) override { impl_->responses_.push({client_id_, id, std::move(result)}); @@ -93,17 +94,17 @@ class TdReceiver { } private: - MultiClient::ClientId client_id_; + ClientManager::ClientId client_id_; TdReceiver *impl_; }; return td::make_unique(client_id, this); } private: - std::queue responses_; + std::queue responses_; }; -class MultiClient::Impl final { +class ClientManager::Impl final { public: Impl() { options_.net_query_stats = std::make_shared(); @@ -120,11 +121,11 @@ class MultiClient::Impl final { return client_id; } - void send(ClientId client_id, RequestId request_id, Function function) { + void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { Request request; request.client_id = client_id; request.id = request_id; - request.function = std::move(function); + request.request = std::move(request); requests_.push_back(std::move(request)); } @@ -134,7 +135,7 @@ class MultiClient::Impl final { for (auto &request : requests_) { auto &td = tds_[request.client_id]; CHECK(!td.empty()); - send_closure_later(td, &Td::request, request.id, std::move(request.function)); + send_closure_later(td, &Td::request, request.id, std::move(request.request)); } requests_.clear(); } @@ -175,9 +176,9 @@ class MultiClient::Impl final { struct Request { ClientId client_id; RequestId id; - Function function; + td_api::object_ptr request; }; - std::vector requests_; + td::vector requests_; unique_ptr concurrent_scheduler_; ClientId client_id_{0}; Td::Options options_; @@ -191,7 +192,7 @@ class Client::Impl final { } void send(Request request) { - impl_.send(client_id_, request.id, std::move(request.function)); + impl_.send(client_id_, request.id, std::move(request.request)); } Response receive(double timeout) { @@ -203,8 +204,8 @@ class Client::Impl final { } private: - MultiClient::Impl impl_; - MultiClient::ClientId client_id_; + ClientManager::Impl impl_; + ClientManager::ClientId client_id_; }; #else @@ -216,21 +217,22 @@ class TdReceiver { output_queue_->init(); } - MultiClient::Response receive(double timeout) { + ClientManager::Response receive(double timeout) { VLOG(td_requests) << "Begin to wait for updates with timeout " << timeout; auto is_locked = receive_lock_.exchange(true); CHECK(!is_locked); auto response = receive_unlocked(timeout); is_locked = receive_lock_.exchange(false); CHECK(is_locked); - VLOG(td_requests) << "End to wait for updates, returning object " << response.id << ' ' << response.object.get(); + VLOG(td_requests) << "End to wait for updates, returning object " << response.request_id << ' ' + << response.object.get(); return response; } - unique_ptr create_callback(MultiClient::ClientId client_id) { + unique_ptr create_callback(ClientManager::ClientId client_id) { class Callback : public TdCallback { public: - explicit Callback(MultiClient::ClientId client_id, std::shared_ptr output_queue) + explicit Callback(ClientManager::ClientId client_id, std::shared_ptr output_queue) : client_id_(client_id), output_queue_(std::move(output_queue)) { } void on_result(uint64 id, td_api::object_ptr result) override { @@ -248,19 +250,19 @@ class TdReceiver { } private: - MultiClient::ClientId client_id_; + ClientManager::ClientId client_id_; std::shared_ptr output_queue_; }; return td::make_unique(client_id, output_queue_); } private: - using OutputQueue = MpscPollableQueue; + using OutputQueue = MpscPollableQueue; std::shared_ptr output_queue_; int output_queue_ready_cnt_{0}; std::atomic receive_lock_{false}; - MultiClient::Response receive_unlocked(double timeout) { + ClientManager::Response receive_unlocked(double timeout) { if (output_queue_ready_cnt_ == 0) { output_queue_ready_cnt_ = output_queue_->reader_wait_nonblock(); } @@ -306,9 +308,10 @@ class MultiImpl { return id; } - void send(MultiClient::ClientId client_id, MultiClient::RequestId request_id, MultiClient::Function function) { + void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id, + td_api::object_ptr &&request) { auto guard = concurrent_scheduler_->get_send_guard(); - send_closure(multi_td_, &MultiTd::send, client_id, request_id, std::move(function)); + send_closure(multi_td_, &MultiTd::send, client_id, request_id, std::move(request)); } void close(int32 td_id) { @@ -367,7 +370,7 @@ class MultiImplPool { std::shared_ptr net_query_stats_ = std::make_shared(); }; -class MultiClient::Impl final { +class ClientManager::Impl final { public: ClientId create_client() { auto impl = pool_.get(); @@ -379,11 +382,11 @@ class MultiClient::Impl final { return client_id; } - void send(ClientId client_id, RequestId request_id, Function function) { + void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { auto lock = impls_mutex_.lock_read().move_as_ok(); auto it = impls_.find(client_id); CHECK(it != impls_.end()); - it->second->send(client_id, request_id, std::move(function)); + it->second->send(client_id, request_id, std::move(request)); } Response receive(double timeout) { @@ -442,7 +445,7 @@ class Client::Impl final { } Client::Response old_res; - old_res.id = res.id; + old_res.id = res.request_id; old_res.object = std::move(res.object); return old_res; } @@ -489,27 +492,27 @@ Client::~Client() = default; Client::Client(Client &&other) = default; Client &Client::operator=(Client &&other) = default; -MultiClient::MultiClient() : impl_(std::make_unique()) { +ClientManager::ClientManager() : impl_(std::make_unique()) { } -MultiClient::ClientId MultiClient::create_client() { +ClientManager::ClientId ClientManager::create_client() { return impl_->create_client(); } -void MultiClient::send(ClientId client_id, RequestId request_id, Function &&function) { - impl_->send(client_id, request_id, std::move(function)); +void ClientManager::send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { + impl_->send(client_id, request_id, std::move(request)); } -MultiClient::Response MultiClient::receive(double timeout) { +ClientManager::Response ClientManager::receive(double timeout) { return impl_->receive(timeout); } -MultiClient::Object MultiClient::execute(Function &&function) { - return Td::static_request(std::move(function)); +td_api::object_ptr ClientManager::execute(td_api::object_ptr &&request) { + return Td::static_request(std::move(request)); } -MultiClient::~MultiClient() = default; -MultiClient::MultiClient(MultiClient &&other) = default; -MultiClient &MultiClient::operator=(MultiClient &&other) = default; +ClientManager::~ClientManager() = default; +ClientManager::ClientManager(ClientManager &&other) = default; +ClientManager &ClientManager::operator=(ClientManager &&other) = default; } // namespace td diff --git a/td/telegram/Client.h b/td/telegram/Client.h index d88c2ccb1..879fe4890 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -22,7 +22,7 @@ namespace td { * The TDLib instance is created for the lifetime of the Client object. * Requests to TDLib can be sent using the Client::send method from any thread. * New updates and responses to requests can be received using the Client::receive method from any thread, - * this function shouldn't be called simultaneously from two different threads. Also note that all updates and + * this function must not be called simultaneously from two different threads. Also note that all updates and * responses to requests should be applied in the same order as they were received, to ensure consistency. * Given this information, it's advisable to call this function from a dedicated thread. * Some service TDLib requests can be executed synchronously from any thread by using the Client::execute method. @@ -84,7 +84,7 @@ class Client final { */ struct Response { /** - * TDLib request identifier, which corresponds to the response or 0 for incoming updates from TDLib. + * TDLib request identifier, which corresponds to the response, or 0 for incoming updates from TDLib. */ std::uint64_t id; @@ -131,34 +131,121 @@ class Client final { std::unique_ptr impl_; }; -// --- EXPERIMENTAL --- -class MultiClient final { +/** + * The future native C++ interface for interaction with TDLib. + * + * The TDLib client instance is created using the ClientManager::create_client method, returning a client identifier. + * Requests to TDLib can be sent using the ClientManager::send method from any thread. + * New updates and responses to requests can be received using the ClientManager::receive method from any thread, + * this function must not be called simultaneously from two different threads. Also note that all updates and + * responses to requests should be applied in the same order as they were received, to ensure consistency. + * Some TDLib requests can be executed synchronously from any thread by using the ClientManager::execute method. + * + * General pattern of usage: + * \code + * td::ClientManager manager; + * auto client_id = manager.create_client(); + * // somehow share the manager and the client_id with other threads, + * // which will be able to send requests via manager.send(client_id, ...) + * + * const double WAIT_TIMEOUT = 10.0; // seconds + * while (true) { + * auto response = manager.receive(WAIT_TIMEOUT); + * if (response.object == nullptr) { + * continue; + * } + * + * if (response.id == 0) { + * // process response.object as an incoming update of type td_api::Update for the client response.client_id + * } else { + * // process response.object as an answer to a request response.request_id for the client response.client_id + * } + * } + * \endcode + */ +class ClientManager final { public: - MultiClient(); + /** + * Creates a new TDLib client manager. + */ + ClientManager(); + /** + * Opaque TDLib client instance identifier. + */ using ClientId = std::int32_t; - using RequestId = std::uint64_t; - using Function = td_api::object_ptr; - using Object = td_api::object_ptr; - struct Response { - ClientId client_id; - RequestId id; - Object object; - }; + /** + * Request identifier. + * Responses to TDLib requests will have the same request id as the corresponding request. + * Updates from TDLib will have request id == 0, incoming requests are thus disallowed to have request id == 0. + */ + using RequestId = std::uint64_t; + + /** + * Creates a new TDLib client and returns its opaque identifier. + */ ClientId create_client(); - void send(ClientId client_id, RequestId request_id, Function &&function); + /** + * Sends request to TDLib. May be called from any thread. + * \param[in] client_id TDLib client instance identifier. + * \param[in] request_id Request identifier. Must be non-zero. + * \param[in] request Request to TDLib. + */ + void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request); + /** + * A response to a request, or an incoming update from TDLib. + */ + struct Response { + /** + * TDLib client instance identifier, for which the response is received. + */ + ClientId client_id; + + /** + * Request identifier, to which the response corresponds, or 0 for incoming updates from TDLib. + */ + RequestId request_id; + + /** + * TDLib API object representing a response to a TDLib request or an incoming update. + */ + td_api::object_ptr object; + }; + + /** + * Receives incoming updates and request responses from TDLib. May be called from any thread, but must not be + * called simultaneously from two different threads. + * \param[in] timeout The maximum number of seconds allowed for this function to wait for new data. + * \return An incoming update or request response. The object returned in the response may be a nullptr + * if the timeout expires. + */ Response receive(double timeout); - static Object execute(Function &&function); + /** + * Synchronously executes TDLib requests. Only a few requests can be executed synchronously. + * May be called from any thread. + * \param[in] request Request to the TDLib. + * \return The request response. + */ + static td_api::object_ptr execute(td_api::object_ptr &&request); - ~MultiClient(); + /** + * Destroys the client manager and all TDLib client instance managed by it. + */ + ~ClientManager(); - MultiClient(MultiClient &&other); + /** + * Move constructor. + */ + ClientManager(ClientManager &&other); - MultiClient &operator=(MultiClient &&other); + /** + * Move assignment operator. + */ + ClientManager &operator=(ClientManager &&other); private: friend class Client; diff --git a/test/tdclient.cpp b/test/tdclient.cpp index 4e56a0c5a..340a0d9b5 100644 --- a/test/tdclient.cpp +++ b/test/tdclient.cpp @@ -917,9 +917,9 @@ TEST(Client, Multi) { ASSERT_EQ(8 * 1000, ok_count.load()); } -TEST(Client, MultiNew) { +TEST(Client, Manager) { td::vector threads; - td::MultiClient client; + td::ClientManager client; int threads_n = 4; int clients_n = 1000; for (int i = 0; i < threads_n; i++) { @@ -937,7 +937,7 @@ TEST(Client, MultiNew) { std::set ids; while (ids.size() != static_cast(threads_n) * clients_n) { auto event = client.receive(10); - if (event.client_id != 0 && event.id == 3) { + if (event.client_id != 0 && event.request_id == 3) { ids.insert(event.client_id); } }