/* 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 */ #pragma once #include "base/binary_guard.h" #include <crl/crl_time.h> #include <crl/crl_object_on_queue.h> #include <QtCore/QThread> namespace base { namespace details { class TimerObject; class TimerObjectWrap { public: explicit TimerObjectWrap(Fn<void()> adjust); ~TimerObjectWrap(); void call( crl::time_type timeout, Qt::TimerType type, FnMut<void()> method); void cancel(); private: void sendEvent(std::unique_ptr<QEvent> event); std::unique_ptr<TimerObject> _value; }; } // namespace details class ConcurrentTimerEnvironment { public: ConcurrentTimerEnvironment(); ~ConcurrentTimerEnvironment(); std::unique_ptr<details::TimerObject> createTimer(Fn<void()> adjust); static void Adjust(); private: void acquire(); void release(); void adjustTimers(); QThread _thread; QObject _adjuster; }; class ConcurrentTimer { public: explicit ConcurrentTimer( Fn<void(FnMut<void()>)> runner, Fn<void()> callback = nullptr); template <typename Object> explicit ConcurrentTimer( crl::weak_on_queue<Object> weak, Fn<void()> callback = nullptr); static Qt::TimerType DefaultType(TimeMs timeout) { constexpr auto kThreshold = TimeMs(1000); return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer; } void setCallback(Fn<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 _running.alive(); } void cancel(); TimeMs remainingTime() const; private: enum class Repeat : unsigned { Interval = 0, SingleShot = 1, }; Fn<void()> createAdjuster(); void start(TimeMs timeout, Qt::TimerType type, Repeat repeat); void adjust(); void cancelAndSchedule(int timeout); void setTimeout(TimeMs timeout); int timeout() const; void timerEvent(); void setRepeat(Repeat repeat) { _repeat = static_cast<unsigned>(repeat); } Repeat repeat() const { return static_cast<Repeat>(_repeat); } Fn<void(FnMut<void()>)> _runner; std::shared_ptr<bool> _guard; // Must be before _object. details::TimerObjectWrap _object; Fn<void()> _callback; base::binary_guard _running; TimeMs _next = 0; int _timeout = 0; Qt::TimerType _type : 2; bool _adjusted : 1; unsigned _repeat : 1; }; template <typename Object> ConcurrentTimer::ConcurrentTimer( crl::weak_on_queue<Object> weak, Fn<void()> callback) : ConcurrentTimer(weak.runner(), std::move(callback)) { } } // namespace base