/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_pts_waiter.h" #include "api/api_updates.h" PtsWaiter::PtsWaiter(not_null owner) : _owner(owner) { } uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue, int32 pts) { return _queue.emplace( uint64(uint32(pts)) << 32 | (++_skippedKey), queue ).first->first; } void PtsWaiter::setWaitingForSkipped(ChannelData *channel, crl::time ms) { if (ms >= 0) { _owner->ptsWaiterStartTimerFor(channel, ms); _waitingForSkipped = true; } else { _waitingForSkipped = false; checkForWaiting(channel); } } void PtsWaiter::setWaitingForShortPoll(ChannelData *channel, crl::time ms) { if (ms >= 0) { _owner->ptsWaiterStartTimerFor(channel, ms); _waitingForShortPoll = true; } else { _waitingForShortPoll = false; checkForWaiting(channel); } } void PtsWaiter::checkForWaiting(ChannelData *channel) { if (!_waitingForSkipped && !_waitingForShortPoll) { _owner->ptsWaiterStartTimerFor(channel, -1); } } void PtsWaiter::applySkippedUpdates(ChannelData *channel) { if (!_waitingForSkipped) { return; } setWaitingForSkipped(channel, -1); if (_queue.empty()) { return; } ++_applySkippedLevel; for (auto i = _queue.cbegin(), e = _queue.cend(); i != e; ++i) { switch (i->second) { case SkippedUpdate: { _owner->applyUpdateNoPtsCheck(_updateQueue[i->first]); } break; case SkippedUpdates: { _owner->applyUpdatesNoPtsCheck(_updatesQueue[i->first]); } break; } } --_applySkippedLevel; clearSkippedUpdates(); } void PtsWaiter::clearSkippedUpdates() { _queue.clear(); _updateQueue.clear(); _updatesQueue.clear(); _applySkippedLevel = 0; } bool PtsWaiter::updated( ChannelData *channel, int32 pts, int32 count, const MTPUpdates &updates) { if (_requesting || _applySkippedLevel) { return true; } else if (pts <= _good && count > 0) { return false; } else if (check(channel, pts, count)) { return true; } _updatesQueue.emplace(ptsKey(SkippedUpdates, pts), updates); return false; } bool PtsWaiter::updated( ChannelData *channel, int32 pts, int32 count, const MTPUpdate &update) { if (_requesting || _applySkippedLevel) { return true; } else if (pts <= _good && count > 0) { return false; } else if (check(channel, pts, count)) { return true; } _updateQueue.emplace(ptsKey(SkippedUpdate, pts), update); return false; } bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 count) { if (_requesting || _applySkippedLevel) { return true; } else if (pts <= _good && count > 0) { return false; } return check(channel, pts, count); } bool PtsWaiter::updateAndApply( ChannelData *channel, int32 pts, int32 count, const MTPUpdates &updates) { if (!updated(channel, pts, count, updates)) { return false; } if (!_waitingForSkipped || _queue.empty()) { // Optimization - no need to put in queue and back. _owner->applyUpdatesNoPtsCheck(updates); } else { _updatesQueue.emplace(ptsKey(SkippedUpdates, pts), updates); applySkippedUpdates(channel); } return true; } bool PtsWaiter::updateAndApply( ChannelData *channel, int32 pts, int32 count, const MTPUpdate &update) { if (!updated(channel, pts, count, update)) { return false; } if (!_waitingForSkipped || _queue.empty()) { // Optimization - no need to put in queue and back. _owner->applyUpdateNoPtsCheck(update); } else { _updateQueue.emplace(ptsKey(SkippedUpdate, pts), update); applySkippedUpdates(channel); } return true; } bool PtsWaiter::updateAndApply( ChannelData *channel, int32 pts, int32 count) { if (!updated(channel, pts, count)) { return false; } applySkippedUpdates(channel); return true; } // Return false if need to save that update and apply later. bool PtsWaiter::check(ChannelData *channel, int32 pts, int32 count) { if (!inited()) { init(pts); return true; } _last = qMax(_last, pts); _count += count; if (_last == _count) { _good = _last; return true; } else if (_last < _count) { setWaitingForSkipped(channel, 1); } else { setWaitingForSkipped(channel, kWaitForSkippedTimeout); } return !count; }