// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include #include #include // A received message lives here from the moment it comes off the socket until the last consumer is // done with it - the writer has stored the image and the republish thread has sent it. Both consumers // hold a shared_ptr to the region, and the region's destructor is what gives the bytes back. struct ReceiveBufferRegion { uint8_t *data = nullptr; size_t size = 0; }; // Bounds the memory a puller holds in received, not yet consumed, messages - by bytes, which is the // quantity that matters, where the queues in front of and behind it count messages. One image of a // large detector is tens of megabytes, so a queue of a thousand of them is a quantity of memory nobody // chose. // // Regions are handed out in arrival order and the space is taken back in arrival order too, which is // what makes it a ring: the writer and the republish thread may finish with messages out of order, so // a region released early is only remembered, and the space is reclaimed once everything received // before it is released as well. Republish holds at most its queue's worth of messages, so that is as // far as the reclaim can lag. // // A message larger than the whole ring goes on the heap instead, so an oversized START or END message // cannot wedge the receiver; everything else waits for space, which is the back-pressure that keeps the // sender from running ahead. class ReceiveRingBuffer { struct State; std::shared_ptr state; public: explicit ReceiveRingBuffer(size_t capacity); // Blocks until the bytes are available; returns nullptr once Stop() has been called. A request // larger than the capacity (so every request of a ring of capacity 0) is served from the heap, // at once and whether stopped or not. std::shared_ptr Allocate(size_t size); // Releases every waiter in Allocate; the regions already handed out stay valid. void Stop(); size_t GetCapacity() const; // Bytes held in live regions right now / at most since construction (heap fallbacks included). size_t GetUtilization() const; size_t GetMaxUtilization() const; };