File indexing completed on 2024-05-19 04:25:05

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 /**
0008  * KisSignalCompressor will never trigger timeout more often than every \p delay ms,
0009  * i.e. \p delay ms is a given lower limit defining the highest frequency.
0010  *
0011  * The current implementation uses a long-running monitor timer to eliminate the
0012  * overhead incurred by restarting and stopping timers with each signal. The
0013  * consequence of this is that the given \p delay ms is not always exactly followed.
0014  *
0015  * KisSignalCompressor makes the following callback guarantees (0 < err <= 1, with
0016  * err == 0 if this is the first signal after a while):
0017  *
0018  * POSTPONE:
0019  *   - timeout after = [0.5 ... 1.0] * \p delay ms.
0020  * FIRST_ACTIVE_POSTPONE_NEXT:
0021  *   - first timeout immediately
0022  *   - postponed timeout after [0.5 ... 1.0] * \p delay ms
0023  * FIRST_ACTIVE:
0024  *   - first timeout immediately
0025  *   - after that [0.5 ... 1.5] * \p delay ms
0026   * FIRST_INACTIVE:
0027  *   - timeout after [0.5 ... 1.5] * \p delay ms
0028  */
0029 
0030 #include "kis_signal_compressor.h"
0031 
0032 #include <QTimer>
0033 #include "kis_assert.h"
0034 #include "kis_debug.h"
0035 
0036 
0037 KisSignalCompressor::KisSignalCompressor()
0038     : QObject(0)
0039     , m_timer(new QTimer(this))
0040 {
0041     m_timer->setSingleShot(false);
0042     connect(m_timer, SIGNAL(timeout()), SLOT(slotTimerExpired()));
0043 }
0044 
0045 KisSignalCompressor::KisSignalCompressor(int delay, Mode mode, QObject *parent)
0046     : KisSignalCompressor(delay, mode, PRECISE_INTERVAL, parent)
0047 {
0048 }
0049 
0050 KisSignalCompressor::KisSignalCompressor(int delay, Mode mode, SlowHandlerMode slowHandlerMode, QObject *parent)
0051     : QObject(parent),
0052       m_timer(new QTimer(this)),
0053       m_mode(mode),
0054       m_slowHandlerMode(slowHandlerMode),
0055       m_timeout(delay)
0056 {
0057     m_timer->setSingleShot(false);
0058     m_timer->setInterval(delay);
0059     connect(m_timer, SIGNAL(timeout()), SLOT(slotTimerExpired()));
0060 }
0061 
0062 void KisSignalCompressor::setDelayImpl(int delay)
0063 {
0064     const bool wasActive = m_timer->isActive();
0065 
0066     if (wasActive) {
0067         m_timer->stop();
0068     }
0069 
0070     m_timer->setInterval(delay);
0071 
0072     if (wasActive) {
0073         m_timer->start();
0074     }
0075 }
0076 
0077 void KisSignalCompressor::setDelay(int delay)
0078 {
0079     m_timeout = delay;
0080     m_idleCallback = {};
0081     setDelayImpl(delay);
0082 }
0083 
0084 void KisSignalCompressor::setDelay(std::function<bool ()> idleCallback, int idleDelay, int timeout)
0085 {
0086     m_timeout = timeout;
0087     m_idleCallback = idleCallback;
0088     setDelayImpl(idleDelay);
0089 }
0090 
0091 void KisSignalCompressor::start()
0092 {
0093     KIS_SAFE_ASSERT_RECOVER_RETURN(m_mode != UNDEFINED);
0094 
0095     const bool isFirstStart = !m_timer->isActive();
0096 
0097     KIS_SAFE_ASSERT_RECOVER_NOOP(!isFirstStart || !m_signalsPending);
0098     m_sanityIsStarting++;
0099 
0100 
0101     switch (m_mode) {
0102     case POSTPONE:
0103         if (isFirstStart) {
0104             m_timer->start();
0105         }
0106         m_lastEmittedTimer.restart();
0107         m_signalsPending = true;
0108         break;
0109     case FIRST_ACTIVE_POSTPONE_NEXT:
0110     case FIRST_ACTIVE:
0111         if (isFirstStart) {
0112             m_timer->start();
0113             if (m_slowHandlerMode == PRECISE_INTERVAL) {
0114                 m_lastEmittedTimer.restart();
0115             }
0116             m_signalsPending = false;
0117             if (!tryEmitSignalSafely()) {
0118                 m_signalsPending = true;
0119             }
0120             if (m_slowHandlerMode == ADDITIVE_INTERVAL) {
0121                 m_lastEmittedTimer.restart();
0122             }
0123         } else {
0124             if (m_mode == FIRST_ACTIVE) {
0125                 m_signalsPending = true;
0126                 tryEmitOnTick(false);
0127             } else {
0128                 m_lastEmittedTimer.restart();
0129                 m_signalsPending = true;
0130             }
0131         }
0132         break;
0133     case FIRST_INACTIVE:
0134         if (isFirstStart) {
0135             m_timer->start();
0136             m_lastEmittedTimer.restart();
0137             m_signalsPending = true;
0138         } else {
0139             m_signalsPending = true;
0140             tryEmitOnTick(false);
0141         }
0142     case UNDEFINED:
0143         ; // Should never happen, but do nothing
0144     };
0145 
0146     m_sanityIsStarting--;
0147 
0148     KIS_SAFE_ASSERT_RECOVER(m_timer->isActive()) {
0149         m_timer->start();
0150     }
0151 }
0152 
0153 bool KisSignalCompressor::tryEmitOnTick(bool isFromTimer)
0154 {
0155     bool wasEmitted = false;
0156 
0157     // we have different requirements for hi-frequency events (the mean
0158     // of the events rate must be min(compressorRate, eventsRate)
0159     const int realInterval = m_timeout;
0160     const int minInterval = realInterval < 100 ? 0.5 * realInterval : realInterval;
0161 
0162     // Enable for debugging:
0163     // ENTER_FUNCTION() << ppVar(isFromTimer) << ppVar(m_signalsPending) << m_lastEmittedTimer.elapsed() << ppVar((m_idleCallback && m_idleCallback()));
0164 
0165     if (m_signalsPending &&
0166             (m_lastEmittedTimer.elapsed() >= minInterval ||
0167              (m_idleCallback && m_idleCallback()))) {
0168 
0169         KIS_SAFE_ASSERT_RECOVER_NOOP(!isFromTimer || !m_isEmitting);
0170 
0171         if (m_slowHandlerMode == PRECISE_INTERVAL) {
0172             m_lastEmittedTimer.start();
0173         }
0174 
0175         m_signalsPending = false;
0176         if (!tryEmitSignalSafely()) {
0177             m_signalsPending = true;
0178         }
0179 
0180         if (m_slowHandlerMode == ADDITIVE_INTERVAL) {
0181             m_lastEmittedTimer.start();
0182         }
0183 
0184         wasEmitted = true;
0185     } else if (!isFromTimer) {
0186         m_signalsPending = true;
0187     }
0188 
0189     return wasEmitted;
0190 }
0191 
0192 bool KisSignalCompressor::tryEmitSignalSafely()
0193 {
0194     bool wasEmitted = false;
0195 
0196     m_isEmitting++;
0197 
0198     if (m_isEmitting == 1) {
0199         emit timeout();
0200         wasEmitted = true;
0201     }
0202 
0203     m_isEmitting--;
0204 
0205     return wasEmitted;
0206 }
0207 
0208 void KisSignalCompressor::slotTimerExpired()
0209 {
0210     KIS_SAFE_ASSERT_RECOVER_NOOP(!m_sanityIsStarting);
0211     KIS_ASSERT_RECOVER_NOOP(m_mode != UNDEFINED);
0212     if (!tryEmitOnTick(true)) {
0213         const int calmDownInterval = 5 * m_timeout;
0214 
0215         if (!m_lastEmittedTimer.isValid() ||
0216             m_lastEmittedTimer.elapsed() > calmDownInterval) {
0217 
0218             m_timer->stop();
0219         }
0220     }
0221 }
0222 
0223 void KisSignalCompressor::stop()
0224 {
0225     m_timer->stop();
0226     m_signalsPending = false;
0227     m_lastEmittedTimer.invalidate();
0228 }
0229 
0230 bool KisSignalCompressor::isActive() const
0231 {
0232     return m_signalsPending && m_timer->isActive();
0233 }
0234 
0235 void KisSignalCompressor::setMode(KisSignalCompressor::Mode mode)
0236 {
0237     m_mode = mode;
0238 }
0239 
0240 int KisSignalCompressor::delay() const
0241 {
0242     return m_timeout;
0243 }