libAttachmentSupport: convert into shared lib

This ensures that the support routines in this library will be
exposed at the same vaddr to each object in the vaddrspace.
This commit is contained in:
2025-11-04 00:22:25 -04:00
parent 7a55a65589
commit 9a4f80a9d6
6 changed files with 22 additions and 9 deletions
@@ -0,0 +1,103 @@
#include <boostAsioLinkageFix.h>
#include <iostream>
#include <config.h>
#include <componentThread.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/system/error_code.hpp>
#include <spinLock.h>
#include <asynchronousBridge.h>
#include <user/stimulusBuffer.h>
namespace smo {
namespace stim_buff {
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 to let in-flight operation finish
delayTimer.expires_from_now(
boost::posix_time::milliseconds(getStopDelayMs()));
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();
std::cout << __func__ << ": Stopped stimulus buffer for device "
<< deviceAttachmentSpec.deviceSelector << std::endl;
// After delay, cancel timer and perform cleanup
timer.cancel();
}
void StimulusBuffer::scheduleNextTimeout(int delayMs)
{
if (!shouldContinue.load())
{ return; }
// Schedule the next timeout using the provided delay
timer.expires_from_now(
boost::posix_time::milliseconds(delayMs));
timer.async_wait(
std::bind(
&StimulusBuffer::onTimeout, this, std::placeholders::_1));
}
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; }
// Call the derived class's frame production handler
stimFrameProductionTimesliceInd();
// 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