Files
salmanoff/include/user/spMcRingBuffer.h
T

106 lines
2.7 KiB
C++
Raw Normal View History

2025-10-31 22:58:18 -04:00
#ifndef _SP_MC_RING_BUFFER_H
#define _SP_MC_RING_BUFFER_H
#include <vector>
#include <cstddef>
#include <stdexcept>
#include <string>
#include <new>
#include <user/stimulusFrame.h>
2025-10-31 22:58:18 -04:00
#include <user/sequenceLock.h>
namespace smo {
namespace stim_buff {
2025-10-31 22:58:18 -04:00
/**
* @brief Single-producer, multi-consumer ring buffer w/per-slot sequence locks
*
* A ring buffer that maintains data alignment constraints while providing
* lock-free read access through per-slot sequence locks. The locks are kept
* separate from the data to preserve alignment requirements for the input
* engine.
*/
class SpMcRingBuffer
{
public:
/** EXPLANATION:
* Constructor initializes the ring buffer with the given constraints and
* number of buffers. Allocates slots vector with properly constructed
* StimulusFrame instances.
2025-10-31 22:58:18 -04:00
*/
explicit SpMcRingBuffer(
size_t nBuffers_,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints,
size_t nSlotsPerStimFrame)
: nBuffers(nBuffers_),
// Default-construct all frames
slots(nBuffers)
2025-10-31 22:58:18 -04:00
{
if (nBuffers == 0)
2025-10-31 22:58:18 -04:00
{
throw std::invalid_argument(std::string(__func__)
+ ": SpMcRingBuffer: nBuffers must be > 0");
2025-10-31 22:58:18 -04:00
}
// Re-invoke constructors w/placement new on default-constructed frames
for (size_t i = 0; i < nBuffers; ++i)
{
slots[i].~StimulusFrame(); // Destroy default-constructed object
new (&slots[i]) StimulusFrame(
inputEngineConstraints, outputEngineConstraints,
nSlotsPerStimFrame);
}
2025-10-31 22:58:18 -04:00
}
~SpMcRingBuffer() = default;
// Non-copyable, non-movable (slots are non-movable)
2025-10-31 22:58:18 -04:00
SpMcRingBuffer(const SpMcRingBuffer&) = delete;
SpMcRingBuffer& operator=(const SpMcRingBuffer&) = delete;
SpMcRingBuffer(SpMcRingBuffer&&) = delete;
SpMcRingBuffer& operator=(SpMcRingBuffer&&) = delete;
2025-10-31 22:58:18 -04:00
public:
/**
* @brief Get a reference to the StimulusFrame at the specified slot
2025-10-31 22:58:18 -04:00
*
* @param slotIndex The index of the slot (0-based)
* @return Reference to StimulusFrame at the slot
* @throws std::out_of_range if slotIndex >= nBuffers
2025-10-31 22:58:18 -04:00
*/
StimulusFrame& getDataAtSlot(size_t slotIndex)
2025-10-31 22:58:18 -04:00
{
if (slotIndex >= nBuffers)
2025-10-31 22:58:18 -04:00
{
throw std::out_of_range(std::string(__func__)
+ ": SpMcRingBuffer: slotIndex must be < nBuffers");
2025-10-31 22:58:18 -04:00
}
return slots[slotIndex];
2025-10-31 22:58:18 -04:00
}
SequenceLock& getSequenceLockAtSlot(size_t slotIndex)
{
if (slotIndex >= nBuffers)
2025-10-31 22:58:18 -04:00
{
throw std::out_of_range(std::string(__func__)
+ ": SpMcRingBuffer: slotIndex must be < nBuffers");
2025-10-31 22:58:18 -04:00
}
return slots[slotIndex].lock;
2025-10-31 22:58:18 -04:00
}
public:
// Layout/invariants
size_t nBuffers;
private:
// Frames vector: each frame contains a sequence lock and staging buffer
std::vector<StimulusFrame> slots;
2025-10-31 22:58:18 -04:00
};
} // namespace stim_buff
2025-10-31 22:58:18 -04:00
} // namespace smo
#endif // _SP_MC_RING_BUFFER_H