| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "base/concurrent_timer.h"
- #include <QtCore/QThread>
- #include <QtCore/QCoreApplication>
- using namespace base::details;
- namespace base {
- namespace details {
- namespace {
- auto CallDelayedEventType() {
- static const auto Result = QEvent::Type(QEvent::registerEventType());
- return Result;
- }
- auto CancelTimerEventType() {
- static const auto Result = QEvent::Type(QEvent::registerEventType());
- return Result;
- }
- ConcurrentTimerEnvironment *Environment/* = nullptr*/;
- QMutex EnvironmentMutex;
- class CallDelayedEvent : public QEvent {
- public:
- CallDelayedEvent(
- crl::time timeout,
- Qt::TimerType type,
- FnMut<void()> method);
- crl::time timeout() const;
- Qt::TimerType type() const;
- FnMut<void()> takeMethod();
- private:
- crl::time _timeout = 0;
- Qt::TimerType _type = Qt::PreciseTimer;
- FnMut<void()> _method;
- };
- class CancelTimerEvent : public QEvent {
- public:
- CancelTimerEvent();
- };
- CallDelayedEvent::CallDelayedEvent(
- crl::time timeout,
- Qt::TimerType type,
- FnMut<void()> method)
- : QEvent(CallDelayedEventType())
- , _timeout(timeout)
- , _type(type)
- , _method(std::move(method)) {
- Expects(_timeout >= 0 && _timeout < std::numeric_limits<int>::max());
- }
- crl::time CallDelayedEvent::timeout() const {
- return _timeout;
- }
- Qt::TimerType CallDelayedEvent::type() const {
- return _type;
- }
- FnMut<void()> CallDelayedEvent::takeMethod() {
- return base::take(_method);
- }
- CancelTimerEvent::CancelTimerEvent() : QEvent(CancelTimerEventType()) {
- }
- } // namespace
- class TimerObject : public QObject {
- public:
- TimerObject(
- not_null<QThread*> thread,
- not_null<QObject*> adjuster,
- Fn<void()> adjust);
- protected:
- bool event(QEvent *e) override;
- private:
- void callDelayed(not_null<CallDelayedEvent*> e);
- void callNow();
- void cancel();
- void adjust();
- FnMut<void()> _next;
- Fn<void()> _adjust;
- int _timerId = 0;
- };
- TimerObject::TimerObject(
- not_null<QThread*> thread,
- not_null<QObject*> adjuster,
- Fn<void()> adjust)
- : _adjust(std::move(adjust)) {
- moveToThread(thread);
- connect(
- adjuster,
- &QObject::destroyed,
- this,
- &TimerObject::adjust,
- Qt::DirectConnection);
- }
- bool TimerObject::event(QEvent *e) {
- const auto type = e->type();
- if (type == CallDelayedEventType()) {
- callDelayed(static_cast<CallDelayedEvent*>(e));
- return true;
- } else if (type == CancelTimerEventType()) {
- cancel();
- return true;
- } else if (type == QEvent::Timer) {
- callNow();
- return true;
- }
- return QObject::event(e);
- }
- void TimerObject::callDelayed(not_null<CallDelayedEvent*> e) {
- cancel();
- const auto timeout = e->timeout();
- const auto type = e->type();
- _next = e->takeMethod();
- if (timeout > 0) {
- _timerId = startTimer(timeout, type);
- } else {
- base::take(_next)();
- }
- }
- void TimerObject::cancel() {
- if (const auto id = base::take(_timerId)) {
- killTimer(id);
- }
- _next = nullptr;
- }
- void TimerObject::callNow() {
- auto next = base::take(_next);
- cancel();
- next();
- }
- void TimerObject::adjust() {
- if (_adjust) {
- _adjust();
- }
- }
- TimerObjectWrap::TimerObjectWrap(Fn<void()> adjust) {
- QMutexLocker lock(&EnvironmentMutex);
- if (Environment) {
- _value = Environment->createTimer(std::move(adjust));
- }
- }
- TimerObjectWrap::~TimerObjectWrap() {
- if (_value) {
- QMutexLocker lock(&EnvironmentMutex);
- if (Environment) {
- _value.release()->deleteLater();
- }
- }
- }
- void TimerObjectWrap::call(
- crl::time timeout,
- Qt::TimerType type,
- FnMut<void()> method) {
- sendEvent(std::make_unique<CallDelayedEvent>(
- timeout,
- type,
- std::move(method)));
- }
- void TimerObjectWrap::cancel() {
- sendEvent(std::make_unique<CancelTimerEvent>());
- }
- void TimerObjectWrap::sendEvent(std::unique_ptr<QEvent> event) {
- if (!_value) {
- return;
- }
- QCoreApplication::postEvent(
- _value.get(),
- event.release(),
- Qt::HighEventPriority);
- }
- } // namespace details
- ConcurrentTimerEnvironment::ConcurrentTimerEnvironment() {
- _thread.setObjectName("Concurrent Timer Thread");
- _thread.start();
- _adjuster.moveToThread(&_thread);
- acquire();
- }
- ConcurrentTimerEnvironment::~ConcurrentTimerEnvironment() {
- _thread.quit();
- release();
- _thread.wait();
- QObject::disconnect(&_adjuster, &QObject::destroyed, nullptr, nullptr);
- }
- std::unique_ptr<TimerObject> ConcurrentTimerEnvironment::createTimer(
- Fn<void()> adjust) {
- return std::make_unique<TimerObject>(
- &_thread,
- &_adjuster,
- std::move(adjust));
- }
- void ConcurrentTimerEnvironment::Adjust() {
- QMutexLocker lock(&EnvironmentMutex);
- if (Environment) {
- Environment->adjustTimers();
- }
- }
- void ConcurrentTimerEnvironment::adjustTimers() {
- QObject emitter;
- QObject::connect(
- &emitter,
- &QObject::destroyed,
- &_adjuster,
- &QObject::destroyed,
- Qt::QueuedConnection);
- }
- void ConcurrentTimerEnvironment::acquire() {
- Expects(Environment == nullptr);
- QMutexLocker lock(&EnvironmentMutex);
- Environment = this;
- }
- void ConcurrentTimerEnvironment::release() {
- Expects(Environment == this);
- QMutexLocker lock(&EnvironmentMutex);
- Environment = nullptr;
- }
- ConcurrentTimer::ConcurrentTimer(
- Fn<void(FnMut<void()>)> runner,
- Fn<void()> callback)
- : _runner(std::move(runner))
- , _object(createAdjuster())
- , _callback(std::move(callback))
- , _type(Qt::PreciseTimer) {
- setRepeat(Repeat::Interval);
- }
- Fn<void()> ConcurrentTimer::createAdjuster() {
- _guard = std::make_shared<bool>(true);
- return [=, runner = _runner, guard = std::weak_ptr<bool>(_guard)] {
- runner([=] {
- if (!guard.lock()) {
- return;
- }
- adjust();
- });
- };
- }
- void ConcurrentTimer::start(
- crl::time timeout,
- Qt::TimerType type,
- Repeat repeat) {
- _type = type;
- setRepeat(repeat);
- _adjusted = false;
- setTimeout(timeout);
- cancelAndSchedule(_timeout);
- _next = crl::now() + _timeout;
- }
- void ConcurrentTimer::cancelAndSchedule(int timeout) {
- auto method = [
- =,
- runner = _runner,
- guard = _running.make_guard()
- ]() mutable {
- if (!guard) {
- return;
- }
- runner([=, guard = std::move(guard)] {
- if (!guard) {
- return;
- }
- timerEvent();
- });
- };
- _object.call(timeout, _type, std::move(method));
- }
- void ConcurrentTimer::timerEvent() {
- if (repeat() == Repeat::Interval) {
- if (_adjusted) {
- start(_timeout, _type, repeat());
- } else {
- _next = crl::now() + _timeout;
- }
- } else {
- cancel();
- }
- if (_callback) {
- _callback();
- }
- }
- void ConcurrentTimer::cancel() {
- _running = {};
- if (isActive()) {
- _running = base::binary_guard();
- _object.cancel();
- }
- }
- crl::time ConcurrentTimer::remainingTime() const {
- if (!isActive()) {
- return -1;
- }
- const auto now = crl::now();
- return (_next > now) ? (_next - now) : crl::time(0);
- }
- void ConcurrentTimer::adjust() {
- auto remaining = remainingTime();
- if (remaining >= 0) {
- cancelAndSchedule(remaining);
- _adjusted = true;
- }
- }
- void ConcurrentTimer::setTimeout(crl::time timeout) {
- Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
- _timeout = static_cast<unsigned int>(timeout);
- }
- int ConcurrentTimer::timeout() const {
- return _timeout;
- }
- } // namespace base
|