File indexing completed on 2024-05-05 17:50:04
0001 /* 0002 Copyright (C) 2014 Andreas Hartmetz <ahartmetz@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LGPL. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 0019 Alternatively, this file is available under the Mozilla Public License 0020 Version 1.1. You may obtain a copy of the License at 0021 http://www.mozilla.org/MPL/ 0022 */ 0023 0024 #include "timer.h" 0025 0026 #include "eventdispatcher.h" 0027 #include "eventdispatcher_p.h" 0028 #include "icompletionlistener.h" 0029 #include "platformtime.h" 0030 0031 #include <algorithm> 0032 #include <cassert> 0033 #include <iostream> 0034 #include <limits> 0035 0036 Timer::Timer(EventDispatcher *dispatcher) 0037 : m_eventDispatcher(dispatcher), 0038 m_completionListener(nullptr), 0039 m_reentrancyGuard(nullptr), 0040 m_interval(0), 0041 m_isRunning(false), 0042 m_isRepeating(true), 0043 m_nextDueTime(0), 0044 m_serial(0) 0045 { 0046 } 0047 0048 Timer::~Timer() 0049 { 0050 // Rationale for "|| m_reentrancyGuard": While triggered, we must be removed from the event 0051 // dispatcher's timer map before it may dereference the then dangling pointer to this Timer. 0052 if (m_isRunning || m_reentrancyGuard) { 0053 EventDispatcherPrivate::get(m_eventDispatcher)->removeTimer(this); 0054 } 0055 0056 if (m_reentrancyGuard) { 0057 *m_reentrancyGuard = false; 0058 m_reentrancyGuard = nullptr; 0059 } 0060 } 0061 0062 void Timer::start(int msec) 0063 { 0064 if (msec < 0) { 0065 std::cerr << "Timer::start(): interval cannot be negative!\n"; 0066 } 0067 // restart if already running 0068 if (!m_reentrancyGuard && m_isRunning) { 0069 EventDispatcherPrivate::get(m_eventDispatcher)->removeTimer(this); 0070 } 0071 m_interval = msec; 0072 m_isRunning = true; 0073 if (!m_reentrancyGuard) { 0074 EventDispatcherPrivate::get(m_eventDispatcher)->addTimer(this); 0075 } 0076 } 0077 0078 void Timer::stop() 0079 { 0080 setRunning(false); 0081 } 0082 0083 void Timer::setRunning(bool run) 0084 { 0085 if (m_isRunning == run) { 0086 return; 0087 } 0088 m_isRunning = run; 0089 if (!m_reentrancyGuard) { 0090 EventDispatcherPrivate *const ep = EventDispatcherPrivate::get(m_eventDispatcher); 0091 if (run) { 0092 ep->addTimer(this); 0093 } else { 0094 ep->removeTimer(this); 0095 } 0096 } 0097 } 0098 0099 bool Timer::isRunning() const 0100 { 0101 return m_isRunning; 0102 } 0103 0104 void Timer::setInterval(int msec) 0105 { 0106 if (msec < 0) { 0107 std::cerr << "Timer::setInterval(): interval cannot be negative!\n"; 0108 } 0109 if (m_interval == msec) { 0110 return; 0111 } 0112 m_interval = msec; 0113 if (m_isRunning && !m_reentrancyGuard) { 0114 EventDispatcherPrivate *const ep = EventDispatcherPrivate::get(m_eventDispatcher); 0115 ep->removeTimer(this); 0116 ep->addTimer(this); 0117 } 0118 } 0119 0120 int Timer::interval() const 0121 { 0122 return m_interval; 0123 } 0124 0125 void Timer::setRepeating(bool repeating) 0126 { 0127 m_isRepeating = repeating; 0128 } 0129 0130 bool Timer::isRepeating() const 0131 { 0132 return m_isRepeating; 0133 } 0134 0135 int Timer::remainingTime() const 0136 { 0137 if (!m_isRunning) { 0138 return -1; 0139 } 0140 const uint64 currentTime = PlatformTime::monotonicMsecs(); 0141 if (currentTime > m_nextDueTime) { 0142 return 0; 0143 } 0144 return std::min(uint64(std::numeric_limits<int>::max()), m_nextDueTime - currentTime); 0145 } 0146 0147 #if defined __GNUC__ && __GNUC__ >= 12 0148 #define GCC_12 0149 #endif 0150 0151 #ifdef GCC_12 0152 #pragma GCC diagnostic push 0153 #pragma GCC diagnostic ignored "-Wdangling-pointer" 0154 #endif 0155 void Timer::trigger() 0156 { 0157 assert(m_isRunning); 0158 if (m_reentrancyGuard) { 0159 return; 0160 } 0161 if (!m_isRepeating) { 0162 m_isRunning = false; 0163 } 0164 0165 // Changes to this timer while in the callback require special treatment. m_reentrancyGuard 0166 // helps provide that. 0167 bool alive = true; 0168 m_reentrancyGuard = &alive; 0169 if (m_completionListener) { 0170 m_completionListener->handleCompletion(this); 0171 } 0172 // if we we've been destroyed, we don't touch the member variable 0173 if (alive) { 0174 assert(m_reentrancyGuard); 0175 m_reentrancyGuard = nullptr; 0176 } 0177 } 0178 #ifdef GCC_12 0179 #pragma GCC diagnostic pop 0180 #endif 0181 0182 void Timer::setCompletionListener(ICompletionListener *client) 0183 { 0184 m_completionListener = client; 0185 } 0186 0187 ICompletionListener *Timer::completionClient() const 0188 { 0189 return m_completionListener; 0190 } 0191 0192 EventDispatcher *Timer::eventDispatcher() const 0193 { 0194 return m_eventDispatcher; 0195 }