File indexing completed on 2024-05-12 15:56:59
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 0099 switch (m_mode) { 0100 case POSTPONE: 0101 if (isFirstStart) { 0102 m_timer->start(); 0103 } 0104 m_lastEmittedTimer.restart(); 0105 m_signalsPending = true; 0106 break; 0107 case FIRST_ACTIVE_POSTPONE_NEXT: 0108 case FIRST_ACTIVE: 0109 if (isFirstStart) { 0110 m_timer->start(); 0111 if (m_slowHandlerMode == PRECISE_INTERVAL) { 0112 m_lastEmittedTimer.restart(); 0113 } 0114 m_signalsPending = false; 0115 if (!tryEmitSignalSafely()) { 0116 m_signalsPending = true; 0117 } 0118 if (m_slowHandlerMode == ADDITIVE_INTERVAL) { 0119 m_lastEmittedTimer.restart(); 0120 } 0121 } else { 0122 if (m_mode == FIRST_ACTIVE) { 0123 m_signalsPending = true; 0124 tryEmitOnTick(false); 0125 } else { 0126 m_lastEmittedTimer.restart(); 0127 m_signalsPending = true; 0128 } 0129 } 0130 break; 0131 case FIRST_INACTIVE: 0132 if (isFirstStart) { 0133 m_timer->start(); 0134 m_lastEmittedTimer.restart(); 0135 m_signalsPending = true; 0136 } else { 0137 m_signalsPending = true; 0138 tryEmitOnTick(false); 0139 } 0140 case UNDEFINED: 0141 ; // Should never happen, but do nothing 0142 }; 0143 0144 KIS_SAFE_ASSERT_RECOVER(m_timer->isActive()) { 0145 m_timer->start(); 0146 } 0147 } 0148 0149 bool KisSignalCompressor::tryEmitOnTick(bool isFromTimer) 0150 { 0151 bool wasEmitted = false; 0152 0153 // we have different requirements for hi-frequency events (the mean 0154 // of the events rate must be min(compressorRate, eventsRate) 0155 const int realInterval = m_timeout; 0156 const int minInterval = realInterval < 100 ? 0.5 * realInterval : realInterval; 0157 0158 // Enable for debugging: 0159 // ENTER_FUNCTION() << ppVar(isFromTimer) << ppVar(m_signalsPending) << m_lastEmittedTimer.elapsed() << ppVar((m_idleCallback && m_idleCallback())); 0160 0161 if (m_signalsPending && 0162 (m_lastEmittedTimer.elapsed() >= minInterval || 0163 (m_idleCallback && m_idleCallback()))) { 0164 0165 KIS_SAFE_ASSERT_RECOVER_NOOP(!isFromTimer || !m_isEmitting); 0166 0167 if (m_slowHandlerMode == PRECISE_INTERVAL) { 0168 m_lastEmittedTimer.start(); 0169 } 0170 0171 m_signalsPending = false; 0172 if (!tryEmitSignalSafely()) { 0173 m_signalsPending = true; 0174 } 0175 0176 if (m_slowHandlerMode == ADDITIVE_INTERVAL) { 0177 m_lastEmittedTimer.start(); 0178 } 0179 0180 wasEmitted = true; 0181 } else if (!isFromTimer) { 0182 m_signalsPending = true; 0183 } 0184 0185 return wasEmitted; 0186 } 0187 0188 bool KisSignalCompressor::tryEmitSignalSafely() 0189 { 0190 bool wasEmitted = false; 0191 0192 m_isEmitting++; 0193 0194 if (m_isEmitting == 1) { 0195 emit timeout(); 0196 wasEmitted = true; 0197 } 0198 0199 m_isEmitting--; 0200 0201 return wasEmitted; 0202 } 0203 0204 void KisSignalCompressor::slotTimerExpired() 0205 { 0206 KIS_ASSERT_RECOVER_NOOP(m_mode != UNDEFINED); 0207 if (!tryEmitOnTick(true)) { 0208 const int calmDownInterval = 5 * m_timeout; 0209 0210 if (!m_lastEmittedTimer.isValid() || 0211 m_lastEmittedTimer.elapsed() > calmDownInterval) { 0212 0213 m_timer->stop(); 0214 } 0215 } 0216 } 0217 0218 void KisSignalCompressor::stop() 0219 { 0220 m_timer->stop(); 0221 m_signalsPending = false; 0222 m_lastEmittedTimer.invalidate(); 0223 } 0224 0225 bool KisSignalCompressor::isActive() const 0226 { 0227 return m_signalsPending && m_timer->isActive(); 0228 } 0229 0230 void KisSignalCompressor::setMode(KisSignalCompressor::Mode mode) 0231 { 0232 m_mode = mode; 0233 } 0234 0235 int KisSignalCompressor::delay() const 0236 { 0237 return m_timeout; 0238 }