Add lambda-based checked timers.

Also ApiWrap is now not a QObject.
This commit is contained in:
John Preston 2017-04-07 15:10:10 +03:00
parent 6b242a982b
commit 34ab04cbe6
8 changed files with 276 additions and 17 deletions

View file

@ -36,15 +36,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace {
constexpr auto kReloadChannelMembersTimeout = 1000; // 1 second wait before reload members in channel after adding
constexpr auto kSaveCloudDraftTimeout = 1000; // save draft to the cloud with 1 sec extra delay
constexpr auto kSmallDelayMs = 5;
} // namespace
ApiWrap::ApiWrap() : _messageDataResolveDelayed([this] { resolveMessageDatas(); }) {
ApiWrap::ApiWrap()
: _messageDataResolveDelayed([this] { resolveMessageDatas(); })
, _webPagesTimer([this] { resolveWebPages(); })
, _draftsSaveTimer([this] { saveDraftsToCloud(); }) {
Window::Theme::Background()->start();
connect(&_webPagesTimer, &QTimer::timeout, [this] { resolveWebPages(); });
connect(&_draftsSaveTimer, &QTimer::timeout, [this] { saveDraftsToCloud(); });
}
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback) {
@ -948,7 +949,7 @@ void ApiWrap::requestNotifySetting(PeerData *peer) {
void ApiWrap::saveDraftToCloudDelayed(History *history) {
_draftsSaveRequestIds.insert(history, 0);
if (!_draftsSaveTimer.isActive()) {
_draftsSaveTimer.start(SaveCloudDraftTimeout);
_draftsSaveTimer.callOnce(kSaveCloudDraftTimeout);
}
}
@ -1263,20 +1264,22 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
void ApiWrap::requestWebPageDelayed(WebPageData *page) {
if (page->pendingTill <= 0) return;
_webPagesPending.insert(page, 0);
int32 left = (page->pendingTill - unixtime()) * 1000;
auto left = (page->pendingTill - unixtime()) * 1000;
if (!_webPagesTimer.isActive() || left <= _webPagesTimer.remainingTime()) {
_webPagesTimer.start((left < 0 ? 0 : left) + 1);
_webPagesTimer.callOnce((left < 0 ? 0 : left) + 1);
}
}
void ApiWrap::clearWebPageRequest(WebPageData *page) {
_webPagesPending.remove(page);
if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) _webPagesTimer.stop();
if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) {
_webPagesTimer.cancel();
}
}
void ApiWrap::clearWebPageRequests() {
_webPagesPending.clear();
_webPagesTimer.stop();
_webPagesTimer.cancel();
}
void ApiWrap::resolveWebPages() {
@ -1342,11 +1345,13 @@ void ApiWrap::resolveWebPages() {
}
}
if (m < INT_MAX) _webPagesTimer.start(m * 1000);
if (m < INT_MAX) {
_webPagesTimer.callOnce(m * 1000);
}
}
void ApiWrap::requestParticipantsCountDelayed(ChannelData *channel) {
QTimer::singleShot(kReloadChannelMembersTimeout, this, [this, channel] {
_participantsCountRequestTimer.call(kReloadChannelMembersTimeout, [this, channel] {
channel->updateFull(true);
});
}

View file

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "base/timer.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
@ -35,7 +36,7 @@ inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Cha
} // namespace Api
class ApiWrap : public QObject, private MTP::Sender {
class ApiWrap : private MTP::Sender {
public:
ApiWrap();
@ -124,6 +125,7 @@ private:
PeerRequests _participantsRequests;
PeerRequests _botsRequests;
base::DelayedCallTimer _participantsCountRequestTimer;
typedef QPair<PeerData*, UserData*> KickRequest;
typedef QMap<KickRequest, mtpRequestId> KickRequests;
@ -132,7 +134,7 @@ private:
QMap<ChannelData*, mtpRequestId> _selfParticipantRequests;
QMap<WebPageData*, mtpRequestId> _webPagesPending;
SingleTimer _webPagesTimer;
base::Timer _webPagesTimer;
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
@ -143,7 +145,7 @@ private:
QMap<PeerData*, mtpRequestId> _notifySettingRequests;
QMap<History*, mtpRequestId> _draftsSaveRequestIds;
SingleTimer _draftsSaveTimer;
base::Timer _draftsSaveTimer;
OrderedSet<mtpRequestId> _stickerSetDisenableRequests;
Stickers::Order _stickersOrder;

View file

@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "autoupdater.h"
#include "window/notifications_manager.h"
#include "messenger.h"
#include "base/timer.h"
namespace {
@ -546,6 +547,7 @@ void adjustSingleTimers() {
if (auto a = application()) {
a->adjustSingleTimers();
}
base::Timer::Adjust();
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE

View file

@ -0,0 +1,141 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "base/timer.h"
namespace base {
namespace {
QObject *TimersAdjuster() {
static QObject adjuster;
return &adjuster;
}
} // namespace
Timer::Timer(base::lambda<void()> callback) : QObject(nullptr)
, _callback(std::move(callback))
, _type(Qt::PreciseTimer)
, _adjusted(false)
, _repeat(Repeat::Interval) {
connect(TimersAdjuster(), &QObject::destroyed, this, [this] { adjust(); }, Qt::QueuedConnection);
}
void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) {
cancel();
_type = type;
_repeat = repeat;
_adjusted = false;
setTimeout(timeout);
_timerId = startTimer(_timeout, _type);
if (_timerId) {
_next = getms(true) + _timeout;
} else {
_next = 0;
}
}
void Timer::cancel() {
if (isActive()) {
killTimer(base::take(_timerId));
}
}
TimeMs Timer::remainingTime() const {
if (!isActive()) {
return -1;
}
auto now = getms(true);
return (_next > now) ? (_next - now) : TimeMs(0);
}
void Timer::Adjust() {
QObject emitter;
connect(&emitter, &QObject::destroyed, TimersAdjuster(), &QObject::destroyed);
}
void Timer::adjust() {
auto remaining = remainingTime();
if (remaining >= 0) {
cancel();
_timerId = startTimer(remaining, _type);
_adjusted = true;
}
}
void Timer::setTimeout(TimeMs timeout) {
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
_timeout = static_cast<unsigned int>(timeout);
}
int Timer::timeout() const {
return _timeout;
}
void Timer::timerEvent(QTimerEvent *e) {
if (_repeat == Repeat::Interval) {
if (_adjusted) {
start(_timeout, _type, _repeat);
} else {
_next = getms(true) + _timeout;
}
} else {
cancel();
}
if (_callback) {
_callback();
}
}
int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type) {
Expects(timeout >= 0);
if (!callback) {
return 0;
}
auto timerId = startTimer(static_cast<int>(timeout), type);
if (timerId) {
_callbacks.emplace(timerId, std::move(callback));
}
return timerId;
}
void DelayedCallTimer::cancel(int callId) {
if (callId) {
killTimer(callId);
_callbacks.erase(callId);
}
}
void DelayedCallTimer::timerEvent(QTimerEvent *e) {
auto timerId = e->timerId();
killTimer(timerId);
auto it = _callbacks.find(timerId);
if (it != _callbacks.end()) {
auto callback = std::move(it->second);
_callbacks.erase(it);
callback();
}
}
} // namespace base

View file

@ -0,0 +1,108 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "base/lambda.h"
#include "base/observer.h"
namespace base {
class Timer final : private QObject {
public:
Timer(base::lambda<void()> callback = base::lambda<void()>());
static Qt::TimerType DefaultType(TimeMs timeout) {
constexpr auto kThreshold = TimeMs(1000);
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
}
void setCallback(base::lambda<void()> callback) {
_callback = std::move(callback);
}
void callOnce(TimeMs timeout) {
callOnce(timeout, DefaultType(timeout));
}
void callEach(TimeMs timeout) {
callEach(timeout, DefaultType(timeout));
}
void callOnce(TimeMs timeout, Qt::TimerType type) {
start(timeout, type, Repeat::SingleShot);
}
void callEach(TimeMs timeout, Qt::TimerType type) {
start(timeout, type, Repeat::Interval);
}
bool isActive() const {
return (_timerId != 0);
}
void cancel();
TimeMs remainingTime() const;
static void Adjust();
protected:
void timerEvent(QTimerEvent *e) override;
private:
enum class Repeat {
Interval = 0,
SingleShot = 1,
};
void start(TimeMs timeout, Qt::TimerType type, Repeat repeat);
void adjust();
void setTimeout(TimeMs timeout);
int timeout() const;
base::lambda<void()> _callback;
TimeMs _next = 0;
int _timeout = 0;
int _timerId = 0;
Qt::TimerType _type : 2;
bool _adjusted : 1;
Repeat _repeat : 1;
};
class DelayedCallTimer final : private QObject {
public:
int call(TimeMs timeout, lambda_once<void()> callback) {
return call(timeout, std::move(callback), Timer::DefaultType(timeout));
}
int call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type);
void cancel(int callId);
protected:
void timerEvent(QTimerEvent *e) override;
private:
std::map<int, lambda_once<void()>> _callbacks; // Better to use flatmap.
};
} // namespace base

View file

@ -124,7 +124,6 @@ enum {
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
SaveCloudDraftIdleTimeout = 14000, // save draft to the cloud after 14 more seconds
SaveCloudDraftTimeout = 1000, // save draft to the cloud with 1 sec extra delay
SaveDraftBeforeQuitTimeout = 1500, // give the app 1.5 secs to save drafts to cloud when quitting
SetOnlineAfterActivity = 30, // user with hidden last seen stays online for such amount of seconds in the interface

View file

@ -362,7 +362,7 @@ private:
}
gsl::not_null<Instance*> _instance;
std::set<RequestWrap, RequestWrapComparator> _requests;
std::set<RequestWrap, RequestWrapComparator> _requests; // Better to use flatmap.
};

View file

@ -10,8 +10,10 @@
<(src_loc)/base/qthelp_url.h
<(src_loc)/base/runtime_composer.cpp
<(src_loc)/base/runtime_composer.h
<(src_loc)/base/task_queue.h
<(src_loc)/base/task_queue.cpp
<(src_loc)/base/task_queue.h
<(src_loc)/base/timer.cpp
<(src_loc)/base/timer.h
<(src_loc)/base/type_traits.h
<(src_loc)/base/variant.h
<(src_loc)/base/virtual_method.h