// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // // 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/telegram/StateManager.h" #include "td/actor/PromiseFuture.h" #include "td/actor/SleepActor.h" #include "td/utils/algorithm.h" #include "td/utils/logging.h" #include "td/utils/Time.h" namespace td { void StateManager::inc_connect() { auto &cnt = get_link_token() == 1 ? connect_cnt_ : connect_proxy_cnt_; cnt++; if (cnt == 1) { loop(); } } void StateManager::dec_connect() { auto &cnt = get_link_token() == 1 ? connect_cnt_ : connect_proxy_cnt_; CHECK(cnt > 0); cnt--; if (cnt == 0) { loop(); } } void StateManager::on_synchronized(bool is_synchronized) { if (sync_flag_ != is_synchronized) { sync_flag_ = is_synchronized; loop(); } if (sync_flag_ && !was_sync_) { was_sync_ = true; auto promises = std::move(wait_first_sync_); reset_to_empty(wait_first_sync_); for (auto &promise : promises) { promise.set_value(Unit()); } } } void StateManager::on_network_updated() { do_on_network(network_type_, true /*inc_generation*/); } void StateManager::on_network(NetType new_network_type) { do_on_network(new_network_type, true /*inc_generation*/); } void StateManager::do_on_network(NetType new_network_type, bool inc_generation) { bool new_network_flag = new_network_type != NetType::None; if (network_flag_ != new_network_flag) { network_flag_ = new_network_flag; loop(); } network_type_ = new_network_type; if (inc_generation) { network_generation_++; } notify_flag(Flag::Network); } void StateManager::on_online(bool is_online) { online_flag_ = is_online; notify_flag(Flag::Online); } void StateManager::on_proxy(bool use_proxy) { use_proxy_ = use_proxy; on_network(network_type_); loop(); } void StateManager::on_logging_out(bool is_logging_out) { is_logging_out_ = is_logging_out; notify_flag(Flag::LoggingOut); } void StateManager::add_callback(unique_ptr callback) { if (callback->on_network(network_type_, network_generation_) && callback->on_online(online_flag_) && callback->on_state(get_real_state()) && callback->on_logging_out(is_logging_out_)) { callbacks_.push_back(std::move(callback)); } } void StateManager::wait_first_sync(Promise<> promise) { if (was_sync_) { return promise.set_value(Unit()); } wait_first_sync_.push_back(std::move(promise)); } void StateManager::close() { stop(); } ConnectionState StateManager::get_real_state() const { if (!network_flag_) { return ConnectionState::WaitingForNetwork; } if (!connect_cnt_) { if (use_proxy_ && !connect_proxy_cnt_) { return ConnectionState::ConnectingToProxy; } return ConnectionState::Connecting; } if (!sync_flag_) { return ConnectionState::Updating; } return ConnectionState::Ready; } void StateManager::notify_flag(Flag flag) { for (auto it = callbacks_.begin(); it != callbacks_.end();) { bool ok = [&] { switch (flag) { case Flag::Online: return (*it)->on_online(online_flag_); case Flag::State: return (*it)->on_state(flush_state_); case Flag::Network: return (*it)->on_network(network_type_, network_generation_); case Flag::LoggingOut: return (*it)->on_logging_out(is_logging_out_); default: UNREACHABLE(); return true; } }(); if (ok) { ++it; } else { it = callbacks_.erase(it); } } } void StateManager::on_network_soft() { if (network_type_ == NetType::Unknown) { LOG(INFO) << "Auto set net_type = Other"; do_on_network(NetType::Other, false /*inc_generation*/); } } void StateManager::start_up() { create_actor("SleepActor", 1, PromiseCreator::event(self_closure(this, &StateManager::on_network_soft))) .release(); loop(); } void StateManager::loop() { auto now = Time::now(); auto state = get_real_state(); if (state != pending_state_) { pending_state_ = state; if (!has_timestamp_) { pending_timestamp_ = now; has_timestamp_ = true; } } if (pending_state_ != flush_state_) { double delay = 0; if (flush_state_ != ConnectionState::Empty) { if (static_cast(pending_state_) > static_cast(flush_state_)) { delay = UP_DELAY; } else { delay = DOWN_DELAY; } if (network_type_ == NetType::Unknown) { delay = 0; } } CHECK(has_timestamp_); if (now >= pending_timestamp_ + delay) { has_timestamp_ = false; flush_state_ = pending_state_; notify_flag(Flag::State); } else { set_timeout_at(pending_timestamp_ + delay); } } else { has_timestamp_ = false; } } } // namespace td