From 018c1f1e1dc68d77b8c184292c961e868bb5d09e Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Fri, 31 Oct 2025 22:58:18 -0400 Subject: [PATCH] SpMcRingBuffer: Added this new class This will be the foundation for all StimulusBuffers. We can most likely add this generically to the StimulusBuffer base class rather than adding it only to StimulusBuffer's derived classes. --- include/user/spMcRingBuffer.h | 137 ++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 include/user/spMcRingBuffer.h diff --git a/include/user/spMcRingBuffer.h b/include/user/spMcRingBuffer.h new file mode 100644 index 0000000..34f6a2a --- /dev/null +++ b/include/user/spMcRingBuffer.h @@ -0,0 +1,137 @@ +#ifndef _SP_MC_RING_BUFFER_H +#define _SP_MC_RING_BUFFER_H + +#include +#include +#include +#include +#include + +namespace smo { + +/** + * @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: + class InputEngineConstraints + { + public: + InputEngineConstraints( + size_t slotStartAlignmentNBytes_, + size_t slotPadToNBytes_) + : slotStartAlignmentNBytes(slotStartAlignmentNBytes_), + slotPadToNBytes(slotPadToNBytes_) + {} + + ~InputEngineConstraints() = default; + + // Input-engine layout/constraints + size_t slotStartAlignmentNBytes; // power-of-2 alignment (e.g., 4096) + size_t slotPadToNBytes; // minimum size per slot + }; + +public: + /** EXPLANATION: + * Constructor initializes the ring buffer with the given constraints and + * number of slots. Calculates stride and allocates data buffer and sequence + * locks array. + */ + explicit SpMcRingBuffer( + const InputEngineConstraints& constraints_, + size_t nSlots_) + : nSlots(nSlots_), strideNBytes(0), bufferNBytes(0), + constraints(constraints_) + { + if (nSlots == 0) + { + throw std::invalid_argument(std::string(__func__) + + ": SpMcRingBuffer: nSlots must be > 0"); + } + + computeStrideAndBufferSize(); + // Allocate data buffer: bufferNBytes (aligned up to alignment) + data.resize(bufferNBytes); + // Initialize sequence locks array: one lock per slot + sequenceLocks.resize(nSlots); + } + + ~SpMcRingBuffer() = default; + + // Non-copyable, movable + SpMcRingBuffer(const SpMcRingBuffer&) = delete; + SpMcRingBuffer& operator=(const SpMcRingBuffer&) = delete; + SpMcRingBuffer(SpMcRingBuffer&&) = default; + SpMcRingBuffer& operator=(SpMcRingBuffer&&) = default; + +public: + /** + * @brief Get a reference to data at the specified slot + * + * @tparam T The type of data stored in the slot + * @param slotIndex The index of the slot (0-based) + * @return Reference to T at the slot + * @throws std::out_of_range if slotIndex >= nSlots + */ + template + T& getDataAtSlot(size_t slotIndex) + { + if (slotIndex >= nSlots) + { + throw std::out_of_range(std::string(__func__) + + ": SpMcRingBuffer: slotIndex must be < nSlots"); + } + + size_t offset = slotIndex * strideNBytes; + return *reinterpret_cast(data.data() + offset); + } + + SequenceLock& getSequenceLockAtSlot(size_t slotIndex) + { + if (slotIndex >= nSlots) + { + throw std::out_of_range(std::string(__func__) + + ": SpMcRingBuffer: slotIndex must be < nSlots"); + } + return sequenceLocks[slotIndex]; + } + +private: + void computeStrideAndBufferSize() + { + // Stride is the maximum of alignment and padding + strideNBytes = std::max( + constraints.slotStartAlignmentNBytes, + constraints.slotPadToNBytes); + + // Buffer size is nSlots * strideNBytes, aligned up to alignment + size_t rawSize = nSlots * strideNBytes; + bufferNBytes = ((rawSize + constraints.slotStartAlignmentNBytes - 1) + / constraints.slotStartAlignmentNBytes) + * constraints.slotStartAlignmentNBytes; + } + + // Buffer data + std::vector data; + + // Sequence locks array: one lock per slot + std::vector sequenceLocks; + +public: + // Layout/invariants + size_t nSlots; + size_t strideNBytes; + size_t bufferNBytes; + InputEngineConstraints constraints; +}; + +} // namespace smo + +#endif // _SP_MC_RING_BUFFER_H +