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 }