From 06618a5253ec7ea849d420d50cfc8843ec843485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Wed, 14 Apr 2021 19:49:17 +0200 Subject: [PATCH] Use fine grained session timers The check of sent requests and containers is done unconditionally every second even though the request timeout is 10 seconds and the container timeout is 600 seconds. This commit uses fine grained timers instead in order to avoid useless system wake-up events. The check of sent requests is now scheduled on demand when a new request is queued. Then the callback, while parsing queued requests, computes the delta to the closest expiring request and automatically schedules the next check if necessary. Given the high value of the container timeout, its callback is called repeatedly every 600 seconds, unless it computes a lower delta for an expiring container using the same logic as for the requests. --- .../SourceFiles/mtproto/session_private.cpp | 38 ++++++++++++++----- .../SourceFiles/mtproto/session_private.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index e7aa978ab9..41c6c57a4d 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -39,7 +39,6 @@ constexpr auto kPingSendAfterForce = 45 * crl::time(1000); constexpr auto kTemporaryExpiresIn = TimeId(86400); constexpr auto kBindKeyAdditionalExpiresTimeout = TimeId(30); constexpr auto kTestModeDcIdShift = 10000; -constexpr auto kCheckSentRequestsEach = 1 * crl::time(1000); constexpr auto kKeyOldEnoughForDestroy = 60 * crl::time(1000); constexpr auto kSentContainerLives = 600 * crl::time(1000); constexpr auto kFastRequestDuration = crl::time(500); @@ -163,13 +162,14 @@ SessionPrivate::SessionPrivate( , _waitForConnected(kMinConnectedTimeout) , _pingSender(thread, [=] { sendPingByTimer(); }) , _checkSentRequestsTimer(thread, [=] { checkSentRequests(); }) +, _clearOldContainersTimer(thread, [=] { clearOldContainers(); }) , _sessionData(std::move(data)) { Expects(_shiftedDcId != 0); moveToThread(thread); InvokeQueued(this, [=] { - _checkSentRequestsTimer.callEach(kCheckSentRequestsEach); + _clearOldContainersTimer.callEach(kSentContainerLives); connectToServer(); }); } @@ -246,41 +246,47 @@ int16 SessionPrivate::getProtocolDcId() const { } void SessionPrivate::checkSentRequests() { - clearOldContainers(); - const auto now = crl::now(); - if (_bindMsgId && _bindMessageSent + kCheckSentRequestTimeout < now) { + const auto checkTime = now - kCheckSentRequestTimeout; + if (_bindMsgId && _bindMessageSent < checkTime) { DEBUG_LOG(("MTP Info: " "Request state while key is not bound, restarting.")); restart(); + _checkSentRequestsTimer.callOnce(kCheckSentRequestTimeout); return; } auto requesting = false; + auto nextTimeout = kCheckSentRequestTimeout; { QReadLocker locker(_sessionData->haveSentMutex()); auto &haveSent = _sessionData->haveSentMap(); - const auto haveSentCount = haveSent.size(); - const auto checkAfter = kCheckSentRequestTimeout; for (const auto &[msgId, request] : haveSent) { - if (request->lastSentTime + checkAfter < now) { + if (request->lastSentTime <= checkTime) { // Need to check state. request->lastSentTime = now; if (_stateRequestData.emplace(msgId).second) { requesting = true; } + } else { + nextTimeout = std::min(request->lastSentTime - checkTime, nextTimeout); } } } if (requesting) { _sessionData->queueSendAnything(kSendStateRequestWaiting); } + if (nextTimeout < kCheckSentRequestTimeout) { + _checkSentRequestsTimer.callOnce(nextTimeout); + } } void SessionPrivate::clearOldContainers() { auto resent = false; + auto nextTimeout = kSentContainerLives; const auto now = crl::now(); + const auto checkTime = now - kSentContainerLives; for (auto i = _sentContainers.begin(); i != _sentContainers.end();) { - if (now > i->second.sent + kSentContainerLives) { + if (i->second.sent <= checkTime) { DEBUG_LOG(("MTP Info: Removing old container with resending %1, " "sent: %2, now: %3, current unixtime: %4" ).arg(i->first @@ -296,12 +302,18 @@ void SessionPrivate::clearOldContainers() { resend(innerMsgId, -1, true); } } else { + nextTimeout = std::min(i->second.sent - checkTime, nextTimeout); ++i; } } if (resent) { _sessionData->queueNeedToResumeAndSend(); } + if (nextTimeout < kSentContainerLives) { + _clearOldContainersTimer.callOnce(nextTimeout); + } else if (!_clearOldContainersTimer.isActive()) { + _clearOldContainersTimer.callEach(nextTimeout); + } } void SessionPrivate::destroyAllConnections() { @@ -683,6 +695,8 @@ void SessionPrivate::tryToSend() { { QWriteLocker locker1(_sessionData->toSendMutex()); + auto scheduleCheckSentRequests = false; + auto toSendDummy = base::flat_map(); auto &toSend = sendAll ? _sessionData->toSendMap() @@ -748,6 +762,7 @@ void SessionPrivate::tryToSend() { QWriteLocker locker2(_sessionData->haveSentMutex()); auto &haveSent = _sessionData->haveSentMap(); haveSent.emplace(msgId, toSendRequest); + scheduleCheckSentRequests = true; const auto wrapLayer = needsLayer && toSendRequest->needsLayer; if (toSendRequest->after) { @@ -871,6 +886,7 @@ void SessionPrivate::tryToSend() { //Assert(!haveSent.contains(msgId)); haveSent.emplace(msgId, request); sentIdsWrap.messages.push_back(msgId); + scheduleCheckSentRequests = true; needAnyResponse = true; } else { _ackedIds.emplace(msgId, request->requestId); @@ -922,6 +938,10 @@ void SessionPrivate::tryToSend() { bigMsgId, forceNewMsgId); _sentContainers.emplace(containerMsgId, std::move(sentIdsWrap)); + + if (scheduleCheckSentRequests && !_checkSentRequestsTimer.isActive()) { + _checkSentRequestsTimer.callOnce(kCheckSentRequestTimeout); + } } } sendSecureRequest(std::move(toSendRequest), needAnyResponse); diff --git a/Telegram/SourceFiles/mtproto/session_private.h b/Telegram/SourceFiles/mtproto/session_private.h index 2c75c45114..b4638ba5fb 100644 --- a/Telegram/SourceFiles/mtproto/session_private.h +++ b/Telegram/SourceFiles/mtproto/session_private.h @@ -218,6 +218,7 @@ private: mtpMsgId _pingMsgId = 0; base::Timer _pingSender; base::Timer _checkSentRequestsTimer; + base::Timer _clearOldContainersTimer; std::shared_ptr _sessionData; std::unique_ptr _options;