From b491964a819f967a77a2781163af9bbaab7f6c8a Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 9 Oct 2020 14:25:06 +0300 Subject: [PATCH 1/8] Store TdReceiver by value. GitOrigin-RevId: 51dbcaf815c5ba1a42539242b1e57b456f188d38 --- td/telegram/Client.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 724988b22..9c04be147 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -80,14 +80,13 @@ class ClientManager::Impl final { options_.net_query_stats = std::make_shared(); concurrent_scheduler_ = make_unique(); concurrent_scheduler_->init(0); - receiver_ = make_unique(); concurrent_scheduler_->start(); } ClientId create_client() { auto client_id = ++client_id_; tds_[client_id] = - concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_->create_callback(client_id), options_); + concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); return client_id; } @@ -101,14 +100,14 @@ class ClientManager::Impl final { for (size_t i = 0; i < requests_.size(); i++) { auto &request = requests_[i]; if (request.client_id <= 0 || request.client_id > client_id_) { - receiver_->add_response(request.client_id, request.id, - td_api::make_object(400, "Invalid TDLib instance specified")); + receiver_.add_response(request.client_id, request.id, + td_api::make_object(400, "Invalid TDLib instance specified")); continue; } auto it = tds_.find(request.client_id); if (it == tds_.end() || it->second.empty()) { - receiver_->add_response(request.client_id, request.id, - td_api::make_object(500, "Request aborted")); + receiver_.add_response(request.client_id, request.id, + td_api::make_object(500, "Request aborted")); continue; } send_closure_later(it->second, &Td::request, request.id, std::move(request.request)); @@ -116,10 +115,10 @@ class ClientManager::Impl final { requests_.clear(); } - auto response = receiver_->receive(0); + auto response = receiver_.receive(0); if (response.client_id == 0) { concurrent_scheduler_->run_main(0); - response = receiver_->receive(0); + response = receiver_.receive(0); } else { ConcurrentScheduler::emscripten_clear_main_timeout(); } @@ -161,7 +160,7 @@ class ClientManager::Impl final { } private: - unique_ptr receiver_; + TdReceiver receiver_; struct Request { ClientId client_id; RequestId id; @@ -410,7 +409,7 @@ class ClientManager::Impl final { public: ClientId create_client() { auto impl = pool_.get(); - auto client_id = impl->create(*receiver_); + auto client_id = impl->create(receiver_); { auto lock = impls_mutex_.lock_write().move_as_ok(); impls_[client_id].impl = std::move(impl); @@ -421,20 +420,20 @@ class ClientManager::Impl final { void send(ClientId client_id, RequestId request_id, td_api::object_ptr &&request) { auto lock = impls_mutex_.lock_read().move_as_ok(); if (!MultiImpl::is_valid_client_id(client_id)) { - receiver_->add_response(client_id, request_id, - td_api::make_object(400, "Invalid TDLib instance specified")); + receiver_.add_response(client_id, request_id, + td_api::make_object(400, "Invalid TDLib instance specified")); return; } auto 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")); + receiver_.add_response(client_id, request_id, td_api::make_object(500, "Request aborted")); return; } it->second.impl->send(client_id, request_id, std::move(request)); } Response receive(double timeout) { - auto response = receiver_->receive(timeout); + auto response = receiver_.receive(timeout); if (response.request_id == 0 && response.object != nullptr && response.object->get_id() == td_api::updateAuthorizationState::ID && static_cast(response.object.get())->authorization_state_->get_id() == @@ -484,7 +483,7 @@ class ClientManager::Impl final { bool is_closed = false; }; std::unordered_map impls_; - unique_ptr receiver_{make_unique()}; + TdReceiver receiver_; }; class Client::Impl final { @@ -492,8 +491,7 @@ class Client::Impl final { Impl() { static MultiImplPool pool; multi_impl_ = pool.get(); - receiver_ = make_unique(); - td_id_ = multi_impl_->create(*receiver_); + td_id_ = multi_impl_->create(receiver_); } void send(Request request) { @@ -506,7 +504,7 @@ class Client::Impl final { } Response receive(double timeout) { - auto response = receiver_->receive(timeout); + auto response = receiver_.receive(timeout); Response old_response; old_response.id = response.request_id; @@ -521,7 +519,7 @@ class Client::Impl final { ~Impl() { multi_impl_->close(td_id_); while (true) { - auto response = receiver_->receive(10.0); + auto response = receiver_.receive(10.0); if (response.object == nullptr && response.client_id != 0 && response.request_id == 0) { break; } @@ -530,7 +528,7 @@ class Client::Impl final { private: std::shared_ptr multi_impl_; - unique_ptr receiver_; + TdReceiver receiver_; int32 td_id_; }; From 38f72b353a9f5e40f1a87be6ee0ff4538fb47765 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 9 Oct 2020 15:39:30 +0300 Subject: [PATCH 2/8] Clear ClientManager when the last Client is closed. GitOrigin-RevId: d2d5194f7ffc59dc8db1196401c80689a8645dac --- td/telegram/Client.cpp | 62 ++++++++++++++++++++++++++++++------ td/telegram/Td.h | 6 ++-- test/tdclient.cpp | 71 +++++++++++++++++++++--------------------- 3 files changed, 91 insertions(+), 48 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 9c04be147..0d3603e38 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -76,14 +76,15 @@ class TdReceiver { class ClientManager::Impl final { public: - Impl() { - options_.net_query_stats = std::make_shared(); - concurrent_scheduler_ = make_unique(); - concurrent_scheduler_->init(0); - concurrent_scheduler_->start(); - } - 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(); + } auto client_id = ++client_id_; tds_[client_id] = concurrent_scheduler_->create_actor_unsafe(0, "Td", receiver_.create_callback(client_id), options_); @@ -96,7 +97,6 @@ class ClientManager::Impl final { Response receive(double timeout) { if (!requests_.empty()) { - auto guard = concurrent_scheduler_->get_main_guard(); for (size_t i = 0; i < requests_.size(); i++) { auto &request = requests_[i]; if (request.client_id <= 0 || request.client_id > client_id_) { @@ -110,13 +110,16 @@ class ClientManager::Impl final { td_api::make_object(500, "Request aborted")); continue; } + + CHECK(concurrent_scheduler_ != nullptr); + auto guard = concurrent_scheduler_->get_main_guard(); send_closure_later(it->second, &Td::request, request.id, std::move(request.request)); } requests_.clear(); } auto response = receiver_.receive(0); - if (response.client_id == 0) { + if (response.client_id == 0 && concurrent_scheduler_ != nullptr) { concurrent_scheduler_->run_main(0); response = receiver_.receive(0); } else { @@ -132,12 +135,22 @@ class ClientManager::Impl final { it->second.reset(); } if (response.object == nullptr && response.client_id != 0 && response.request_id == 0) { + CHECK(concurrent_scheduler_ != nullptr); auto guard = concurrent_scheduler_->get_main_guard(); auto it = tds_.find(response.client_id); CHECK(it != tds_.end()); CHECK(it->second.empty()); tds_.erase(it); response.client_id = 0; + + if (tds_.empty()) { + CHECK(options_.net_query_stats.use_count() == 1); + CHECK(options_.net_query_stats->get_count() == 0); + options_.net_query_stats = nullptr; + concurrent_scheduler_->finish(); + concurrent_scheduler_ = nullptr; + reset_to_empty(tds_); + } } return response; } @@ -147,6 +160,10 @@ class ClientManager::Impl final { Impl(Impl &&) = delete; Impl &operator=(Impl &&) = delete; ~Impl() { + if (concurrent_scheduler_ == nullptr) { + return; + } + { auto guard = concurrent_scheduler_->get_main_guard(); for (auto &td : tds_) { @@ -388,6 +405,8 @@ class MultiImplPool { init_openssl_threads(); impls_.resize(clamp(thread::hardware_concurrency(), 8u, 1000u) * 5 / 4); + + net_query_stats_ = std::make_shared(); } auto &impl = *std::min_element(impls_.begin(), impls_.end(), [](auto &a, auto &b) { return a.lock().use_count() < b.lock().use_count(); }); @@ -399,10 +418,28 @@ class MultiImplPool { return result; } + void try_clear() { + std::unique_lock lock(mutex_); + if (impls_.empty()) { + return; + } + + for (auto &impl : impls_) { + if (impl.lock().use_count() != 0) { + return; + } + } + reset_to_empty(impls_); + + CHECK(net_query_stats_.use_count() == 1); + CHECK(net_query_stats_->get_count() == 0); + net_query_stats_ = nullptr; + } + private: std::mutex mutex_; std::vector> impls_; - std::shared_ptr net_query_stats_ = std::make_shared(); + std::shared_ptr net_query_stats_; }; class ClientManager::Impl final { @@ -448,6 +485,11 @@ class ClientManager::Impl final { CHECK(it->second.is_closed); impls_.erase(it); response.client_id = 0; + + if (impls_.empty()) { + reset_to_empty(impls_); + pool_.try_clear(); + } } return response; } diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 8ce1777f2..cbed223c8 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -260,13 +260,13 @@ class Td final : public NetQueryCallback { void dec_stop_cnt(); + unique_ptr callback_; + Options td_options_; + MtprotoHeader::Options options_; TdParameters parameters_; - unique_ptr callback_; - Options td_options_; - StateManager::State connection_state_; std::unordered_multiset request_set_; diff --git a/test/tdclient.cpp b/test/tdclient.cpp index 21dc98429..f1a58b8bc 100644 --- a/test/tdclient.cpp +++ b/test/tdclient.cpp @@ -1092,7 +1092,6 @@ TEST(Client, ManagerClose) { #endif TEST(Client, ManagerCloseOneThread) { - SET_VERBOSITY_LEVEL(2); td::ClientManager client_manager; td::uint64 request_id = 2; @@ -1133,42 +1132,44 @@ TEST(Client, ManagerCloseOneThread) { } }; - for (td::int32 i = -5; i <= 0; i++) { - send_request(i, 400); + for (int t = 0; t < 3; t++) { + for (td::int32 i = -5; i <= 0; i++) { + send_request(i, 400); + } + + receive(); + + auto client_id = client_manager.create_client(); + + for (td::int32 i = -5; i < 5; i++) { + send_request(i, i == client_id ? 0 : (i > 0 && i < client_id ? 500 : 400)); + } + + receive(); + + for (int i = 0; i < 10; i++) { + send_request(client_id, 0); + } + + receive(); + + sent_count++; + sent_requests.emplace(1, 0); + client_manager.send(client_id, 1, td::make_tl_object()); + + for (int i = 0; i < 10; i++) { + send_request(client_id, 500); + } + + receive(); + + for (int i = 0; i < 10; i++) { + send_request(client_id, 500); + } + + receive(); } - receive(); - - auto client_id = client_manager.create_client(); - - for (td::int32 i = -5; i < 5; i++) { - send_request(i, i == client_id ? 0 : (i > 0 && i < client_id ? 500 : 400)); - } - - receive(); - - for (int i = 0; i < 10; i++) { - send_request(client_id, 0); - } - - receive(); - - sent_count++; - sent_requests.emplace(1, 0); - client_manager.send(client_id, 1, td::make_tl_object()); - - for (int i = 0; i < 10; i++) { - send_request(client_id, 500); - } - - receive(); - - for (int i = 0; i < 10; i++) { - send_request(client_id, 500); - } - - receive(); - ASSERT_TRUE(sent_requests.empty()); } From 30471b7099cb2d96bcabf303923f18af69699d52 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 9 Oct 2020 15:42:57 +0300 Subject: [PATCH 3/8] Do not send chat actions to offline users like the main Android client. GitOrigin-RevId: 6299f69ade490fa10bad74779b30a0d73a145ff2 --- td/telegram/ContactsManager.cpp | 10 ++++++++++ td/telegram/ContactsManager.h | 4 ++++ td/telegram/MessagesManager.cpp | 14 ++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index beb08dc75..deec1976e 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -12229,6 +12229,11 @@ bool ContactsManager::is_user_deleted(UserId user_id) const { return u == nullptr || u->is_deleted; } +bool ContactsManager::is_user_support(UserId user_id) const { + auto u = get_user(user_id); + return u != nullptr && !u->is_deleted && u->is_support; +} + bool ContactsManager::is_user_bot(UserId user_id) const { auto u = get_user(user_id); return u != nullptr && !u->is_deleted && u->is_bot; @@ -12260,6 +12265,11 @@ Result ContactsManager::get_bot_data(UserId user_id) const { return bot_data; } +bool ContactsManager::is_user_online(UserId user_id) const { + int32 was_online = get_user_was_online(get_user(user_id), user_id); + return was_online > G()->unix_time(); +} + bool ContactsManager::is_user_status_exact(UserId user_id) const { auto u = get_user(user_id); return u != nullptr && !u->is_deleted && !u->is_bot && u->was_online > 0; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 6b6a31216..c43beeeee 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -424,9 +424,13 @@ class ContactsManager : public Actor { bool is_user_deleted(UserId user_id) const; + bool is_user_support(UserId user_id) const; + bool is_user_bot(UserId user_id) const; Result get_bot_data(UserId user_id) const TD_WARN_UNUSED_RESULT; + bool is_user_online(UserId user_id) const; + bool is_user_status_exact(UserId user_id) const; bool can_report_user(UserId user_id) const; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 3d59f4f71..6d1e5ece0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -28853,10 +28853,20 @@ bool MessagesManager::is_dialog_action_unneeded(DialogId dialog_id) const { UserId user_id = dialog_type == DialogType::User ? dialog_id.get_user_id() : td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - if (!user_id.is_valid() || td_->contacts_manager_->is_user_bot(user_id) || - td_->contacts_manager_->is_user_deleted(user_id)) { + if (td_->contacts_manager_->is_user_deleted(user_id)) { return true; } + if (td_->contacts_manager_->is_user_bot(user_id) && !td_->contacts_manager_->is_user_support(user_id)) { + return true; + } + if (user_id == td_->contacts_manager_->get_my_id()) { + return true; + } + + if (!td_->auth_manager_->is_bot() && !td_->contacts_manager_->is_user_online(user_id)) { + return true; + } + if (!td_->auth_manager_->is_bot() && !td_->contacts_manager_->is_user_status_exact(user_id)) { // return true; } From cebc6c1fb4740ee3274a165dfee39f43ed1d1a68 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 9 Oct 2020 18:00:38 +0300 Subject: [PATCH 4/8] Remove empty requests logging. GitOrigin-RevId: fdc488bfee7e974f5aca3322d7d028ed797b312f --- td/telegram/Td.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 84254431c..665f7d34a 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3412,7 +3412,6 @@ void Td::request(uint64 id, tl_object_ptr function) { request_set_.insert(id); if (function == nullptr) { - LOG(ERROR) << "Receive empty request"; return send_error_impl(id, make_error(400, "Request is empty")); } @@ -3511,7 +3510,6 @@ void Td::request(uint64 id, tl_object_ptr function) { td_api::object_ptr Td::static_request(td_api::object_ptr function) { if (function == nullptr) { - LOG(ERROR) << "Receive empty static request"; return td_api::make_object(400, "Request is empty"); } From 84ea5b06c9cdfef2bca48ede72abace29bef232e Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 10 Oct 2020 22:02:59 +0300 Subject: [PATCH 5/8] Minor. GitOrigin-RevId: 1e722a9e8ad6ddd894f9fd4e1b05529aa4aa6a15 --- td/telegram/CountryInfoManager.cpp | 1 - td/telegram/MessagesManager.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/td/telegram/CountryInfoManager.cpp b/td/telegram/CountryInfoManager.cpp index 89ebfa13d..bbb500d58 100644 --- a/td/telegram/CountryInfoManager.cpp +++ b/td/telegram/CountryInfoManager.cpp @@ -313,7 +313,6 @@ void CountryInfoManager::on_get_country_list(const string &language_code, void CountryInfoManager::on_get_country_list_impl(const string &language_code, tl_object_ptr country_list) { - LOG(ERROR) << to_string(country_list); CHECK(country_list != nullptr); auto &countries = countries_[language_code]; switch (country_list->get_id()) { diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6d1e5ece0..78b6792e2 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -22168,7 +22168,7 @@ MessageId MessagesManager::get_reply_to_message_id(Dialog *d, MessageId top_thre return message_id; } if (top_thread_message_id.is_valid() && top_thread_message_id.is_server() && - get_message_force(d, top_thread_message_id, "get_reply_to_message_id 1") != nullptr) { + get_message_force(d, top_thread_message_id, "get_reply_to_message_id 3") != nullptr) { return top_thread_message_id; } From 2850e596d374c3b04da8a0763074d6e409d34566 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 10 Oct 2020 22:05:20 +0300 Subject: [PATCH 6/8] Fix ConcurrentScheduler guard usage. GitOrigin-RevId: 6f4dd470a690cdbe98870c89433634b31136f832 --- td/telegram/Client.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 0d3603e38..2fb8be272 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -129,14 +129,13 @@ class ClientManager::Impl final { response.object->get_id() == td_api::updateAuthorizationState::ID && static_cast(response.object.get())->authorization_state_->get_id() == td_api::authorizationStateClosed::ID) { + CHECK(concurrent_scheduler_ != nullptr); auto guard = concurrent_scheduler_->get_main_guard(); auto it = tds_.find(response.client_id); CHECK(it != tds_.end()); it->second.reset(); } if (response.object == nullptr && response.client_id != 0 && response.request_id == 0) { - CHECK(concurrent_scheduler_ != nullptr); - auto guard = concurrent_scheduler_->get_main_guard(); auto it = tds_.find(response.client_id); CHECK(it != tds_.end()); CHECK(it->second.empty()); From c484cc477344ce7179b5df8387e34dac3497c705 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 10 Oct 2020 22:08:41 +0300 Subject: [PATCH 7/8] Postpone td_api::authorizationStateClosed until the client is fully closed. GitOrigin-RevId: 064696a00110c1f567b6da6afbee58d868ff4938 --- td/telegram/Client.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 2fb8be272..78b8d9a1e 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -134,13 +134,18 @@ class ClientManager::Impl final { auto it = tds_.find(response.client_id); CHECK(it != tds_.end()); it->second.reset(); + + response.client_id = 0; + response.object = nullptr; } if (response.object == nullptr && response.client_id != 0 && response.request_id == 0) { auto it = tds_.find(response.client_id); CHECK(it != tds_.end()); CHECK(it->second.empty()); tds_.erase(it); - response.client_id = 0; + + response.object = td_api::make_object( + td_api::make_object()); if (tds_.empty()) { CHECK(options_.net_query_stats.use_count() == 1); @@ -476,6 +481,9 @@ class ClientManager::Impl final { td_api::authorizationStateClosed::ID) { auto lock = impls_mutex_.lock_write().move_as_ok(); close_impl(response.client_id); + + response.client_id = 0; + response.object = nullptr; } if (response.object == nullptr && response.client_id != 0 && response.request_id == 0) { auto lock = impls_mutex_.lock_write().move_as_ok(); @@ -483,7 +491,9 @@ class ClientManager::Impl final { CHECK(it != impls_.end()); CHECK(it->second.is_closed); impls_.erase(it); - response.client_id = 0; + + response.object = td_api::make_object( + td_api::make_object()); if (impls_.empty()) { reset_to_empty(impls_); From 7207d76a809598d019e17fb04b2edc46789c6e22 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 10 Oct 2020 22:37:36 +0300 Subject: [PATCH 8/8] Add ExitGuard. GitOrigin-RevId: f8f04daacbee00386e326eb3ca1ec3dfec19cbb0 --- tdutils/CMakeLists.txt | 2 ++ tdutils/td/utils/ExitGuard.cpp | 13 +++++++++++++ tdutils/td/utils/ExitGuard.h | 32 ++++++++++++++++++++++++++++++++ tdutils/test/misc.cpp | 19 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 tdutils/td/utils/ExitGuard.cpp create mode 100644 tdutils/td/utils/ExitGuard.h diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 57c1ac34a..d3b12e6f3 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -87,6 +87,7 @@ set(TDUTILS_SOURCE td/utils/BufferedUdp.cpp td/utils/check.cpp td/utils/crypto.cpp + td/utils/ExitGuard.cpp td/utils/FileLog.cpp td/utils/filesystem.cpp td/utils/find_boundary.cpp @@ -187,6 +188,7 @@ set(TDUTILS_SOURCE td/utils/Destructor.h td/utils/Enumerator.h td/utils/EpochBasedMemoryReclamation.h + td/utils/ExitGuard.h td/utils/FileLog.h td/utils/filesystem.h td/utils/find_boundary.h diff --git a/tdutils/td/utils/ExitGuard.cpp b/tdutils/td/utils/ExitGuard.cpp new file mode 100644 index 000000000..1c62dec5e --- /dev/null +++ b/tdutils/td/utils/ExitGuard.cpp @@ -0,0 +1,13 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// 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/ExitGuard.h" + +namespace td { + +std::atomic ExitGuard::is_exited_{false}; + +} // namespace td diff --git a/tdutils/td/utils/ExitGuard.h b/tdutils/td/utils/ExitGuard.h new file mode 100644 index 000000000..1a91a731f --- /dev/null +++ b/tdutils/td/utils/ExitGuard.h @@ -0,0 +1,32 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// 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 ExitGuard { + public: + ExitGuard() = default; + ExitGuard(ExitGuard &&) = delete; + ExitGuard &operator=(ExitGuard &&) = delete; + ExitGuard(const ExitGuard &) = delete; + ExitGuard &operator=(const ExitGuard &) = delete; + ~ExitGuard() { + is_exited_.store(true, std::memory_order_relaxed); + } + + static bool is_exited() { + return is_exited_.load(std::memory_order_relaxed); + } + + private: + static std::atomic is_exited_; +}; + +} // namespace td diff --git a/tdutils/test/misc.cpp b/tdutils/test/misc.cpp index cc62bdb37..a3def7ed2 100644 --- a/tdutils/test/misc.cpp +++ b/tdutils/test/misc.cpp @@ -10,6 +10,7 @@ #include "td/utils/bits.h" #include "td/utils/CancellationToken.h" #include "td/utils/common.h" +#include "td/utils/ExitGuard.h" #include "td/utils/Hash.h" #include "td/utils/HashMap.h" #include "td/utils/HashSet.h" @@ -50,6 +51,24 @@ using namespace td; +struct CheckExitGuard { + explicit CheckExitGuard(bool expected_value): expected_value_(expected_value) { + } + CheckExitGuard(CheckExitGuard &&) = delete; + CheckExitGuard &operator=(CheckExitGuard &&) = delete; + CheckExitGuard(const CheckExitGuard &) = delete; + CheckExitGuard &operator=(const CheckExitGuard &) = delete; + ~CheckExitGuard() { + ASSERT_EQ(expected_value_, ExitGuard::is_exited()); + } + + bool expected_value_; +}; + +CheckExitGuard check_exit_guard_true{true}; +ExitGuard exit_guard; +CheckExitGuard check_exit_guard_false{false}; + #if TD_LINUX || TD_DARWIN TEST(Misc, update_atime_saves_mtime) { SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));