File indexing completed on 2024-12-01 10:31:52
0001 /* 0002 * qca_safetimer.cpp - Qt Cryptographic Architecture 0003 * Copyright (C) 2014 Ivan Romanov <drizt@land.ru> 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Lesser General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2.1 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Lesser General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Lesser General Public 0016 * License along with this library; if not, write to the Free Software 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0018 * 02110-1301 USA 0019 * 0020 */ 0021 0022 #include "qca_safetimer.h" 0023 #include <QElapsedTimer> 0024 #include <QTimerEvent> 0025 #include <qmath.h> 0026 0027 // #define SAFETIMER_DEBUG 0028 0029 #ifdef SAFETIMER_DEBUG 0030 #include <QDebug> 0031 #endif 0032 0033 namespace QCA { 0034 0035 class SafeTimer::Private : public QObject 0036 { 0037 Q_OBJECT 0038 friend class SafeTimer; 0039 0040 public: 0041 Private(QObject *parent = nullptr); 0042 0043 int timerId; 0044 int fixerTimerId; 0045 bool isSingleShot; 0046 int interval; 0047 bool isActive; 0048 QElapsedTimer elapsedTimer; 0049 0050 public Q_SLOTS: 0051 void fixTimer(); 0052 0053 Q_SIGNALS: 0054 void needFix(); 0055 0056 protected: 0057 bool event(QEvent *event) override; 0058 void timerEvent(QTimerEvent *event) override; 0059 }; 0060 0061 SafeTimer::Private::Private(QObject *parent) 0062 : QObject(parent) 0063 , timerId(0) 0064 , fixerTimerId(0) 0065 , isSingleShot(false) 0066 , interval(0) 0067 , isActive(false) 0068 , elapsedTimer(QElapsedTimer()) 0069 { 0070 connect(this, &Private::needFix, this, &Private::fixTimer, Qt::QueuedConnection); 0071 } 0072 0073 void SafeTimer::Private::fixTimer() 0074 { 0075 // Start special timer to align ressurected old timer 0076 const int msec = qMax(0, interval - static_cast<int>(elapsedTimer.elapsed())); 0077 0078 fixerTimerId = startTimer(msec); 0079 #ifdef SAFETIMER_DEBUG 0080 qDebug() << "START FIXTIMER: id =" << fixerTimerId << ", thread =" << thread() << ", interval =" << msec 0081 << parent(); 0082 #endif 0083 } 0084 0085 bool SafeTimer::Private::event(QEvent *event) 0086 { 0087 if (event->type() == QEvent::ThreadChange && fixerTimerId /* timer is actived */) { 0088 // Timer dies when an object changes owner thread. This trick 0089 // used to ressurect old timer in the new thread. 0090 // Signal is emited in the old thread but will be gotten in the new one. 0091 #ifdef SAFETIMER_DEBUG 0092 qDebug() << "STOP FIXTIMER ON CHANGE THREAD: id =" << fixerTimerId << ", thread =" << thread() << parent(); 0093 #endif 0094 killTimer(fixerTimerId); 0095 fixerTimerId = 0; 0096 emit needFix(); 0097 } 0098 0099 return QObject::event(event); 0100 } 0101 0102 void SafeTimer::Private::timerEvent(QTimerEvent *event) 0103 { 0104 if (event->timerId() == fixerTimerId) { 0105 #ifdef SAFETIMER_DEBUG 0106 qDebug() << "STOP FIXTIMER ON TIMEOUT: id =" << fixerTimerId << ", thread =" << thread() << parent(); 0107 #endif 0108 killTimer(fixerTimerId); 0109 fixerTimerId = 0; 0110 0111 SafeTimer *safeTimer = qobject_cast<SafeTimer *>(parent()); 0112 // Emulate timeout signal of not yet ressurected timer 0113 emit safeTimer->timeout(); 0114 // Ressurect timer here if not a singleshot 0115 if (!isSingleShot) 0116 safeTimer->start(); 0117 else 0118 isActive = false; 0119 } else { 0120 #ifdef SAFETIMER_DEBUG 0121 qDebug() << "BAD PRIVATE TIME EVENT: id =" << timerId << ", thread =" << thread() << this 0122 << ", badId =" << event->timerId() << parent(); 0123 #endif 0124 } 0125 } 0126 0127 SafeTimer::SafeTimer(QObject *parent) 0128 : QObject() 0129 , d(new Private()) 0130 { 0131 // It must be done here. Initialization list can't be used. 0132 // Need to have proper class name. Look at TimerFixer::hook. 0133 setParent(parent); 0134 d->setParent(this); 0135 } 0136 0137 SafeTimer::~SafeTimer() 0138 { 0139 } 0140 0141 int SafeTimer::interval() const 0142 { 0143 return d->interval; 0144 } 0145 0146 bool SafeTimer::isActive() const 0147 { 0148 return d->isActive; 0149 } 0150 0151 bool SafeTimer::isSingleShot() const 0152 { 0153 return d->isSingleShot; 0154 } 0155 0156 void SafeTimer::setInterval(int msec) 0157 { 0158 d->interval = msec; 0159 } 0160 0161 void SafeTimer::setSingleShot(bool singleShot) 0162 { 0163 d->isSingleShot = singleShot; 0164 } 0165 0166 void SafeTimer::start(int msec) 0167 { 0168 d->interval = msec; 0169 start(); 0170 } 0171 0172 void SafeTimer::start() 0173 { 0174 stop(); 0175 0176 d->elapsedTimer.start(); 0177 d->timerId = QObject::startTimer(d->interval); 0178 d->isActive = d->timerId > 0; 0179 0180 #ifdef SAFETIMER_DEBUG 0181 qDebug() << "START TIMER: id =" << d->timerId << ", thread =" << thread() << ", interval =" << d->interval << this; 0182 #endif 0183 } 0184 0185 void SafeTimer::stop() 0186 { 0187 if (d->timerId) { 0188 QObject::killTimer(d->timerId); 0189 #ifdef SAFETIMER_DEBUG 0190 qDebug() << "STOP TIMER: id =" << d->timerId << ", thread =" << thread() << this; 0191 #endif 0192 d->timerId = 0; 0193 } 0194 0195 if (d->fixerTimerId) { 0196 #ifdef SAFETIMER_DEBUG 0197 qDebug() << "STOP FIXER TIMER: id =" << d->fixerTimerId << ", thread =" << thread() << this; 0198 #endif 0199 d->killTimer(d->fixerTimerId); 0200 d->fixerTimerId = 0; 0201 } 0202 d->isActive = false; 0203 } 0204 0205 bool SafeTimer::event(QEvent *event) 0206 { 0207 if (event->type() == QEvent::ThreadChange && d->timerId /* timer is actived */) { 0208 // Timer dies when an object changes owner thread. This trick 0209 // used to ressurect old timer in the new thread. 0210 // Signal is emited in the old thread but will be gotten in the new one. 0211 #ifdef SAFETIMER_DEBUG 0212 qDebug() << "CHANGE THREAD: id =" << d->timerId << ", thread =" << thread() << this; 0213 #endif 0214 killTimer(d->timerId); 0215 d->timerId = 0; 0216 emit d->needFix(); 0217 } 0218 0219 return QObject::event(event); 0220 } 0221 0222 void SafeTimer::timerEvent(QTimerEvent *event) 0223 { 0224 if (event->timerId() == d->timerId) { 0225 if (d->isSingleShot) 0226 stop(); 0227 emit timeout(); 0228 } else { 0229 #ifdef SAFETIMER_DEBUG 0230 qDebug() << "BAD TIME EVENT: id =" << d->timerId << ", thread =" << thread() << this 0231 << ", badId =" << event->timerId() << this; 0232 #endif 0233 } 0234 } 0235 0236 } // end namespace QCA 0237 0238 #include "qca_safetimer.moc"