#ifndef _SP_MC_RING_BUFFER_H #define _SP_MC_RING_BUFFER_H #include #include #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 FrameAssemblyDesc. * Allocates frames vector with properly constructed StimulusFrame instances, * each initialized with a SlotDesc from the FrameAssemblyDesc. */ explicit SpMcRingBuffer( const std::shared_ptr &frameAssemblyDesc_) : nBuffers(frameAssemblyDesc_ ? frameAssemblyDesc_->slots.size() : 0), frameAssemblyDesc(frameAssemblyDesc_), slots(nBuffers) // Default-construct all frames { if (!frameAssemblyDesc) { throw std::invalid_argument(std::string(__func__) + ": SpMcRingBuffer: frameAssemblyDesc must not be null"); } if (nBuffers == 0) { throw std::invalid_argument(std::string(__func__) + ": SpMcRingBuffer: frameAssemblyDesc must have at least one " "slot"); } // 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(frameAssemblyDesc->slots[i]); } } ~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: // FrameAssemblyDesc describing the memory layout std::shared_ptr frameAssemblyDesc; // Frames vector: each frame contains a sequence lock and SlotDesc std::vector slots; }; } // namespace stim_buff } // namespace smo #endif // _SP_MC_RING_BUFFER_H