#ifndef _SP_MC_RING_BUFFER_H #define _SP_MC_RING_BUFFER_H #include #include #include #include #include #include #include namespace smo { namespace stim_buff { /** * @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. */ explicit SpMcRingBuffer( size_t nBuffers_, const StagingBuffer::IOEngineConstraints& inputEngineConstraints, const StagingBuffer::IOEngineConstraints& outputEngineConstraints, size_t nSlotsPerStimFrame) : nBuffers(nBuffers_), // Default-construct all frames slots(nBuffers) { if (nBuffers == 0) { throw std::invalid_argument(std::string(__func__) + ": SpMcRingBuffer: nBuffers must be > 0"); } // 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); } } ~SpMcRingBuffer() = default; // Non-copyable, non-movable (slots are non-movable) SpMcRingBuffer(const SpMcRingBuffer&) = delete; SpMcRingBuffer& operator=(const SpMcRingBuffer&) = delete; SpMcRingBuffer(SpMcRingBuffer&&) = delete; SpMcRingBuffer& operator=(SpMcRingBuffer&&) = delete; public: /** * @brief Get a reference to the StimulusFrame at the specified slot * * @param slotIndex The index of the slot (0-based) * @return Reference to StimulusFrame at the slot * @throws std::out_of_range if slotIndex >= nBuffers */ StimulusFrame& getDataAtSlot(size_t slotIndex) { if (slotIndex >= nBuffers) { throw std::out_of_range(std::string(__func__) + ": SpMcRingBuffer: slotIndex must be < nBuffers"); } return slots[slotIndex]; } SequenceLock& getSequenceLockAtSlot(size_t slotIndex) { if (slotIndex >= nBuffers) { throw std::out_of_range(std::string(__func__) + ": SpMcRingBuffer: slotIndex must be < nBuffers"); } return slots[slotIndex].lock; } public: // Layout/invariants size_t nBuffers; private: // Frames vector: each frame contains a sequence lock and staging buffer std::vector slots; }; } // namespace stim_buff } // namespace smo #endif // _SP_MC_RING_BUFFER_H