diff --git a/include/user/stimulusBuffer.h b/include/user/stimulusBuffer.h index 0719a1d..1f56d36 100644 --- a/include/user/stimulusBuffer.h +++ b/include/user/stimulusBuffer.h @@ -69,33 +69,10 @@ public: scheduleNextTimeout(); } - void stop() - { - shouldContinue.store(false); + void stop(); - // Set up a timeout bridge using the io_service - boost::asio::deadline_timer delayTimer(ioService); - AsynchronousBridge bridge(ioService); - - // Set up the delay for CONFIG_STIMBUFF_FRAME_PERIOD_MS to let in-flight - // operation finish - delayTimer.expires_from_now( - boost::posix_time::milliseconds(CONFIG_STIMBUFF_FRAME_PERIOD_MS)); - - delayTimer.async_wait( - [&bridge](const boost::system::error_code& error) - { - (void)error; - - // Always signal complete, whether timeout expired or was cancelled - bridge.setAsyncOperationComplete(); - }); - - bridge.waitForAsyncOperationCompleteOrIoServiceStopped(); - - // After delay, cancel timer and perform cleanup - timer.cancel(); - } +private: + void onTimeout(const boost::system::error_code& error); public: device::DeviceAttachmentSpec deviceAttachmentSpec; @@ -122,49 +99,80 @@ private: std::bind( &StimulusBuffer::onTimeout, this, std::placeholders::_1)); } - - void onTimeout(const boost::system::error_code& error) - { - // Timer was cancelled, which is expected when stopping - if (error == boost::asio::error::operation_aborted) { - return; - } - - if (error) - { - std::cerr << "StimulusBuffer: Timer error: " << error.message() - << std::endl; - return; - } - - if (!shouldContinue.load()) - { return; } - - /** EXPLANATION: - * We need to ensure that there's only ever one stimframe being produced - * during any CONFIG_STIMBUFF_FRAME_PERIOD_MS period. To guarantee this, we - * use a spinlock. - * - * When a new frame is to be produced, the async producer will first acquire - * the frameAssemblyLimiter spinlock. This way, when the next timeout is - * fired it can check whether its predecessor stimframe has finished being - * produced. If the preceding stimframe is still being produced, then we'll - * sleep for CONFIG_STIMBUFF_FRAME_RETRY_DELAY_MS ms before trying again. - */ - int nextWakeupDelayMs; - if (frameAssemblyRateLimiter.tryAcquire()) - { nextWakeupDelayMs = CONFIG_STIMBUFF_FRAME_PERIOD_MS; } - else - { nextWakeupDelayMs = CONFIG_STIMBUFF_FRAME_RETRY_DELAY_MS; } - - // Placeholder handler (empty for now) - // Note: The lock should be released when frame production completes - - // Schedule next timeout with the pre-determined duration - scheduleNextTimeout(nextWakeupDelayMs); - } }; +/** Inline methods + ******************************************************************************/ + +inline void StimulusBuffer::stop() +{ + shouldContinue.store(false); + + // Set up a timeout bridge using the io_service + boost::asio::deadline_timer delayTimer(ioService); + AsynchronousBridge bridge(ioService); + + // Set up the delay for CONFIG_STIMBUFF_FRAME_PERIOD_MS to let in-flight + // operation finish + delayTimer.expires_from_now( + boost::posix_time::milliseconds(CONFIG_STIMBUFF_FRAME_PERIOD_MS)); + + delayTimer.async_wait( + [&bridge](const boost::system::error_code& error) + { + (void)error; + + // Always signal complete, whether timeout expired or was cancelled + bridge.setAsyncOperationComplete(); + }); + + bridge.waitForAsyncOperationCompleteOrIoServiceStopped(); + + // After delay, cancel timer and perform cleanup + timer.cancel(); +} + +inline void StimulusBuffer::onTimeout(const boost::system::error_code& error) +{ + // Timer was cancelled, which is expected when stopping + if (error == boost::asio::error::operation_aborted) { + return; + } + + if (error) + { + std::cerr << "StimulusBuffer: Timer error: " << error.message() + << std::endl; + return; + } + + if (!shouldContinue.load()) + { return; } + + /** EXPLANATION: + * We need to ensure that there's only ever one stimframe being produced + * during any CONFIG_STIMBUFF_FRAME_PERIOD_MS period. To guarantee this, we + * use a spinlock. + * + * When a new frame is to be produced, the async producer will first acquire + * the frameAssemblyLimiter spinlock. This way, when the next timeout is + * fired it can check whether its predecessor stimframe has finished being + * produced. If the preceding stimframe is still being produced, then we'll + * sleep for CONFIG_STIMBUFF_FRAME_RETRY_DELAY_MS ms before trying again. + */ + int nextWakeupDelayMs; + if (frameAssemblyRateLimiter.tryAcquire()) + { nextWakeupDelayMs = CONFIG_STIMBUFF_FRAME_PERIOD_MS; } + else + { nextWakeupDelayMs = CONFIG_STIMBUFF_FRAME_RETRY_DELAY_MS; } + + // Placeholder handler (empty for now) + // Note: The lock should be released when frame production completes + + // Schedule next timeout with the pre-determined duration + scheduleNextTimeout(nextWakeupDelayMs); +} + } // namespace stim_buff } // namespace smo