concurrent_timer.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "base/concurrent_timer.h"
  8. #include <QtCore/QThread>
  9. #include <QtCore/QCoreApplication>
  10. using namespace base::details;
  11. namespace base {
  12. namespace details {
  13. namespace {
  14. auto CallDelayedEventType() {
  15. static const auto Result = QEvent::Type(QEvent::registerEventType());
  16. return Result;
  17. }
  18. auto CancelTimerEventType() {
  19. static const auto Result = QEvent::Type(QEvent::registerEventType());
  20. return Result;
  21. }
  22. ConcurrentTimerEnvironment *Environment/* = nullptr*/;
  23. QMutex EnvironmentMutex;
  24. class CallDelayedEvent : public QEvent {
  25. public:
  26. CallDelayedEvent(
  27. crl::time timeout,
  28. Qt::TimerType type,
  29. FnMut<void()> method);
  30. crl::time timeout() const;
  31. Qt::TimerType type() const;
  32. FnMut<void()> takeMethod();
  33. private:
  34. crl::time _timeout = 0;
  35. Qt::TimerType _type = Qt::PreciseTimer;
  36. FnMut<void()> _method;
  37. };
  38. class CancelTimerEvent : public QEvent {
  39. public:
  40. CancelTimerEvent();
  41. };
  42. CallDelayedEvent::CallDelayedEvent(
  43. crl::time timeout,
  44. Qt::TimerType type,
  45. FnMut<void()> method)
  46. : QEvent(CallDelayedEventType())
  47. , _timeout(timeout)
  48. , _type(type)
  49. , _method(std::move(method)) {
  50. Expects(_timeout >= 0 && _timeout < std::numeric_limits<int>::max());
  51. }
  52. crl::time CallDelayedEvent::timeout() const {
  53. return _timeout;
  54. }
  55. Qt::TimerType CallDelayedEvent::type() const {
  56. return _type;
  57. }
  58. FnMut<void()> CallDelayedEvent::takeMethod() {
  59. return base::take(_method);
  60. }
  61. CancelTimerEvent::CancelTimerEvent() : QEvent(CancelTimerEventType()) {
  62. }
  63. } // namespace
  64. class TimerObject : public QObject {
  65. public:
  66. TimerObject(
  67. not_null<QThread*> thread,
  68. not_null<QObject*> adjuster,
  69. Fn<void()> adjust);
  70. protected:
  71. bool event(QEvent *e) override;
  72. private:
  73. void callDelayed(not_null<CallDelayedEvent*> e);
  74. void callNow();
  75. void cancel();
  76. void adjust();
  77. FnMut<void()> _next;
  78. Fn<void()> _adjust;
  79. int _timerId = 0;
  80. };
  81. TimerObject::TimerObject(
  82. not_null<QThread*> thread,
  83. not_null<QObject*> adjuster,
  84. Fn<void()> adjust)
  85. : _adjust(std::move(adjust)) {
  86. moveToThread(thread);
  87. connect(
  88. adjuster,
  89. &QObject::destroyed,
  90. this,
  91. &TimerObject::adjust,
  92. Qt::DirectConnection);
  93. }
  94. bool TimerObject::event(QEvent *e) {
  95. const auto type = e->type();
  96. if (type == CallDelayedEventType()) {
  97. callDelayed(static_cast<CallDelayedEvent*>(e));
  98. return true;
  99. } else if (type == CancelTimerEventType()) {
  100. cancel();
  101. return true;
  102. } else if (type == QEvent::Timer) {
  103. callNow();
  104. return true;
  105. }
  106. return QObject::event(e);
  107. }
  108. void TimerObject::callDelayed(not_null<CallDelayedEvent*> e) {
  109. cancel();
  110. const auto timeout = e->timeout();
  111. const auto type = e->type();
  112. _next = e->takeMethod();
  113. if (timeout > 0) {
  114. _timerId = startTimer(timeout, type);
  115. } else {
  116. base::take(_next)();
  117. }
  118. }
  119. void TimerObject::cancel() {
  120. if (const auto id = base::take(_timerId)) {
  121. killTimer(id);
  122. }
  123. _next = nullptr;
  124. }
  125. void TimerObject::callNow() {
  126. auto next = base::take(_next);
  127. cancel();
  128. next();
  129. }
  130. void TimerObject::adjust() {
  131. if (_adjust) {
  132. _adjust();
  133. }
  134. }
  135. TimerObjectWrap::TimerObjectWrap(Fn<void()> adjust) {
  136. QMutexLocker lock(&EnvironmentMutex);
  137. if (Environment) {
  138. _value = Environment->createTimer(std::move(adjust));
  139. }
  140. }
  141. TimerObjectWrap::~TimerObjectWrap() {
  142. if (_value) {
  143. QMutexLocker lock(&EnvironmentMutex);
  144. if (Environment) {
  145. _value.release()->deleteLater();
  146. }
  147. }
  148. }
  149. void TimerObjectWrap::call(
  150. crl::time timeout,
  151. Qt::TimerType type,
  152. FnMut<void()> method) {
  153. sendEvent(std::make_unique<CallDelayedEvent>(
  154. timeout,
  155. type,
  156. std::move(method)));
  157. }
  158. void TimerObjectWrap::cancel() {
  159. sendEvent(std::make_unique<CancelTimerEvent>());
  160. }
  161. void TimerObjectWrap::sendEvent(std::unique_ptr<QEvent> event) {
  162. if (!_value) {
  163. return;
  164. }
  165. QCoreApplication::postEvent(
  166. _value.get(),
  167. event.release(),
  168. Qt::HighEventPriority);
  169. }
  170. } // namespace details
  171. ConcurrentTimerEnvironment::ConcurrentTimerEnvironment() {
  172. _thread.setObjectName("Concurrent Timer Thread");
  173. _thread.start();
  174. _adjuster.moveToThread(&_thread);
  175. acquire();
  176. }
  177. ConcurrentTimerEnvironment::~ConcurrentTimerEnvironment() {
  178. _thread.quit();
  179. release();
  180. _thread.wait();
  181. QObject::disconnect(&_adjuster, &QObject::destroyed, nullptr, nullptr);
  182. }
  183. std::unique_ptr<TimerObject> ConcurrentTimerEnvironment::createTimer(
  184. Fn<void()> adjust) {
  185. return std::make_unique<TimerObject>(
  186. &_thread,
  187. &_adjuster,
  188. std::move(adjust));
  189. }
  190. void ConcurrentTimerEnvironment::Adjust() {
  191. QMutexLocker lock(&EnvironmentMutex);
  192. if (Environment) {
  193. Environment->adjustTimers();
  194. }
  195. }
  196. void ConcurrentTimerEnvironment::adjustTimers() {
  197. QObject emitter;
  198. QObject::connect(
  199. &emitter,
  200. &QObject::destroyed,
  201. &_adjuster,
  202. &QObject::destroyed,
  203. Qt::QueuedConnection);
  204. }
  205. void ConcurrentTimerEnvironment::acquire() {
  206. Expects(Environment == nullptr);
  207. QMutexLocker lock(&EnvironmentMutex);
  208. Environment = this;
  209. }
  210. void ConcurrentTimerEnvironment::release() {
  211. Expects(Environment == this);
  212. QMutexLocker lock(&EnvironmentMutex);
  213. Environment = nullptr;
  214. }
  215. ConcurrentTimer::ConcurrentTimer(
  216. Fn<void(FnMut<void()>)> runner,
  217. Fn<void()> callback)
  218. : _runner(std::move(runner))
  219. , _object(createAdjuster())
  220. , _callback(std::move(callback))
  221. , _type(Qt::PreciseTimer) {
  222. setRepeat(Repeat::Interval);
  223. }
  224. Fn<void()> ConcurrentTimer::createAdjuster() {
  225. _guard = std::make_shared<bool>(true);
  226. return [=, runner = _runner, guard = std::weak_ptr<bool>(_guard)] {
  227. runner([=] {
  228. if (!guard.lock()) {
  229. return;
  230. }
  231. adjust();
  232. });
  233. };
  234. }
  235. void ConcurrentTimer::start(
  236. crl::time timeout,
  237. Qt::TimerType type,
  238. Repeat repeat) {
  239. _type = type;
  240. setRepeat(repeat);
  241. _adjusted = false;
  242. setTimeout(timeout);
  243. cancelAndSchedule(_timeout);
  244. _next = crl::now() + _timeout;
  245. }
  246. void ConcurrentTimer::cancelAndSchedule(int timeout) {
  247. auto method = [
  248. =,
  249. runner = _runner,
  250. guard = _running.make_guard()
  251. ]() mutable {
  252. if (!guard) {
  253. return;
  254. }
  255. runner([=, guard = std::move(guard)] {
  256. if (!guard) {
  257. return;
  258. }
  259. timerEvent();
  260. });
  261. };
  262. _object.call(timeout, _type, std::move(method));
  263. }
  264. void ConcurrentTimer::timerEvent() {
  265. if (repeat() == Repeat::Interval) {
  266. if (_adjusted) {
  267. start(_timeout, _type, repeat());
  268. } else {
  269. _next = crl::now() + _timeout;
  270. }
  271. } else {
  272. cancel();
  273. }
  274. if (_callback) {
  275. _callback();
  276. }
  277. }
  278. void ConcurrentTimer::cancel() {
  279. _running = {};
  280. if (isActive()) {
  281. _running = base::binary_guard();
  282. _object.cancel();
  283. }
  284. }
  285. crl::time ConcurrentTimer::remainingTime() const {
  286. if (!isActive()) {
  287. return -1;
  288. }
  289. const auto now = crl::now();
  290. return (_next > now) ? (_next - now) : crl::time(0);
  291. }
  292. void ConcurrentTimer::adjust() {
  293. auto remaining = remainingTime();
  294. if (remaining >= 0) {
  295. cancelAndSchedule(remaining);
  296. _adjusted = true;
  297. }
  298. }
  299. void ConcurrentTimer::setTimeout(crl::time timeout) {
  300. Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
  301. _timeout = static_cast<unsigned int>(timeout);
  302. }
  303. int ConcurrentTimer::timeout() const {
  304. return _timeout;
  305. }
  306. } // namespace base