From 6a9cd7263680b2f73424cdf4611f1082c1c78174 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 12 Nov 2020 14:45:18 +0300 Subject: [PATCH] Don't create client through ClientManager until first request is sent to it. --- td/telegram/Client.cpp | 77 +++++++++++++++++++++------------- td/telegram/Client.h | 1 + td/telegram/ClientJson.cpp | 8 ++-- td/telegram/ClientJson.h | 8 ++-- td/telegram/td_json_client.cpp | 8 ++-- td/telegram/td_json_client.h | 4 +- test/tdclient.cpp | 6 ++- 7 files changed, 66 insertions(+), 46 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 9cc4cf9a9..c56fdd82a 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -78,22 +78,25 @@ class TdReceiver { class ClientManager::Impl final { public: ClientId create_client() { - if (tds_.empty()) { - CHECK(concurrent_scheduler_ == nullptr); - CHECK(options_.net_query_stats == nullptr); - options_.net_query_stats = std::make_shared(); - concurrent_scheduler_ = make_unique(); - concurrent_scheduler_->init(0); - concurrent_scheduler_->start(); - } CHECK(client_id_ != std::numeric_limits::max()); auto client_id = ++client_id_; - tds_[client_id] = - concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); + pending_clients_.insert(client_id); return client_id; } void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { + if (pending_clients_.erase(client_id) != 0) { + if (tds_.empty()) { + CHECK(concurrent_scheduler_ == nullptr); + CHECK(options_.net_query_stats == nullptr); + options_.net_query_stats = std::make_shared(); + concurrent_scheduler_ = make_unique(); + concurrent_scheduler_->init(0); + concurrent_scheduler_->start(); + } + tds_[client_id] = + concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); + } requests_.push_back({client_id, request_id, std::move(request)}); } @@ -196,6 +199,7 @@ class ClientManager::Impl final { unique_ptr concurrent_scheduler_; ClientId client_id_{0}; Td::Options options_; + std::unordered_set pending_clients_; std::unordered_map> tds_; }; @@ -356,10 +360,15 @@ class MultiImpl { MultiImpl(MultiImpl &&) = delete; MultiImpl &operator=(MultiImpl &&) = delete; - int32 create(TdReceiver &receiver) { - auto id = create_id(); - create(id, receiver.create_callback(id)); - return id; + static int32 create_id() { + auto result = current_id_.fetch_add(1); + CHECK(result <= static_cast(std::numeric_limits::max())); + return static_cast(result); + } + + void create(int32 td_id, unique_ptr callback) { + auto guard = concurrent_scheduler_->get_send_guard(); + send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback)); } static bool is_valid_client_id(int32 client_id) { @@ -395,17 +404,6 @@ class MultiImpl { ActorOwn multi_td_; static std::atomic current_id_; - - static int32 create_id() { - auto result = current_id_.fetch_add(1); - CHECK(result <= static_cast(std::numeric_limits::max())); - return static_cast(result); - } - - void create(int32 td_id, unique_ptr callback) { - auto guard = concurrent_scheduler_->get_send_guard(); - send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback)); - } }; std::atomic MultiImpl::current_id_{1}; @@ -458,11 +456,10 @@ class MultiImplPool { class ClientManager::Impl final { public: ClientId create_client() { - auto impl = pool_.get(); - auto client_id = impl->create(receiver_); + auto client_id = MultiImpl::create_id(); { auto lock = impls_mutex_.lock_write().move_as_ok(); - impls_[client_id].impl = std::move(impl); + impls_[client_id]; // create empty MultiImplInfo } return client_id; } @@ -474,7 +471,22 @@ class ClientManager::Impl final { td_api::make_object(400, "Invalid TDLib instance specified")); return; } + auto it = impls_.find(client_id); + if (it != impls_.end() && it->second.impl == nullptr) { + lock.reset(); + + auto write_lock = impls_mutex_.lock_write().move_as_ok(); + it = impls_.find(client_id); + if (it != impls_.end() && it->second.impl == nullptr) { + it->second.impl = pool_.get(); + it->second.impl->create(client_id, receiver_.create_callback(client_id)); + } + write_lock.reset(); + + lock = impls_mutex_.lock_read().move_as_ok(); + it = impls_.find(client_id); + } if (it == impls_.end() || it->second.is_closed) { receiver_.add_response(client_id, request_id, td_api::make_object(500, "Request aborted")); return; @@ -517,7 +529,11 @@ class ClientManager::Impl final { CHECK(it != impls_.end()); if (!it->second.is_closed) { it->second.is_closed = true; - it->second.impl->close(client_id); + if (it->second.impl == nullptr) { + receiver_.add_response(client_id, 0, nullptr); + } else { + it->second.impl->close(client_id); + } } } @@ -554,7 +570,8 @@ class Client::Impl final { Impl() { static MultiImplPool pool; multi_impl_ = pool.get(); - td_id_ = multi_impl_->create(receiver_); + td_id_ = MultiImpl::create_id(); + multi_impl_->create(td_id_, receiver_.create_callback(td_id_)); } void send(Request request) { diff --git a/td/telegram/Client.h b/td/telegram/Client.h index 7cb386def..ee1e2d755 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -184,6 +184,7 @@ class ClientManager final { /** * Creates a new TDLib client and returns its opaque identifier. + * The client will not send updates until the first request is sent to it. */ ClientId create_client(); diff --git a/td/telegram/ClientJson.cpp b/td/telegram/ClientJson.cpp index 1e4f37c57..960b55207 100644 --- a/td/telegram/ClientJson.cpp +++ b/td/telegram/ClientJson.cpp @@ -125,11 +125,11 @@ static std::mutex extra_mutex; static std::unordered_map extra; static std::atomic extra_id{1}; -int td_json_create_client() { +int json_create_client() { return static_cast(get_manager()->create_client()); } -void td_json_send(int client_id, Slice request) { +void json_send(int client_id, Slice request) { auto parsed_request = to_request(request); auto request_id = extra_id.fetch_add(1, std::memory_order_relaxed); if (!parsed_request.second.empty()) { @@ -139,7 +139,7 @@ void td_json_send(int client_id, Slice request) { get_manager()->send(client_id, request_id, std::move(parsed_request.first)); } -const char *td_json_receive(double timeout) { +const char *json_receive(double timeout) { auto response = get_manager()->receive(timeout); if (!response.object) { return nullptr; @@ -157,7 +157,7 @@ const char *td_json_receive(double timeout) { return store_string(from_response(*response.object, extra_str, response.client_id)); } -const char *td_json_execute(Slice request) { +const char *json_execute(Slice request) { auto parsed_request = to_request(request); return store_string( from_response(*ClientManager::execute(std::move(parsed_request.first)), parsed_request.second, 0)); diff --git a/td/telegram/ClientJson.h b/td/telegram/ClientJson.h index 9421b1d33..d7bc85125 100644 --- a/td/telegram/ClientJson.h +++ b/td/telegram/ClientJson.h @@ -33,12 +33,12 @@ class ClientJson final { std::atomic extra_id_{1}; }; -int td_json_create_client(); +int json_create_client(); -void td_json_send(int client_id, Slice request); +void json_send(int client_id, Slice request); -const char *td_json_receive(double timeout); +const char *json_receive(double timeout); -const char *td_json_execute(Slice request); +const char *json_execute(Slice request); } // namespace td diff --git a/td/telegram/td_json_client.cpp b/td/telegram/td_json_client.cpp index 3ddb5945c..c346a9048 100644 --- a/td/telegram/td_json_client.cpp +++ b/td/telegram/td_json_client.cpp @@ -31,17 +31,17 @@ const char *td_json_client_execute(void *client, const char *request) { } int td_create_client() { - return td::td_json_create_client(); + return td::json_create_client(); } void td_send(int client_id, const char *request) { - td::td_json_send(client_id, td::Slice(request == nullptr ? "" : request)); + td::json_send(client_id, td::Slice(request == nullptr ? "" : request)); } const char *td_receive(double timeout) { - return td::td_json_receive(timeout); + return td::json_receive(timeout); } const char *td_execute(const char *request) { - return td::td_json_execute(td::Slice(request == nullptr ? "" : request)); + return td::json_execute(td::Slice(request == nullptr ? "" : request)); } diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index 918e40522..f98705210 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -126,8 +126,8 @@ TDJSON_EXPORT void td_json_client_destroy(void *client); */ /** - * Creates a new instance of TDLib. - * \return Opaque indentifier of the created TDLib client. + * Creates a new instance of TDLib. The TDLib instance will not send updates until the first request is sent to it. + * \return Opaque indentifier of the created TDLib instance. */ TDJSON_EXPORT int td_create_client(); diff --git a/test/tdclient.cpp b/test/tdclient.cpp index 5217b33d8..800108b82 100644 --- a/test/tdclient.cpp +++ b/test/tdclient.cpp @@ -941,9 +941,11 @@ TEST(Client, Manager) { client.send(-1, 3, td::make_tl_object(3)); for (int i = 0; i < threads_n; i++) { threads.emplace_back([&] { - for (int i = 0; i < clients_n; i++) { + for (int i = 0; i <= clients_n; i++) { auto id = client.create_client(); - client.send(id, 3, td::make_tl_object(3)); + if (i != 0) { + client.send(id, 3, td::make_tl_object(3)); + } } }); }