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 }