class CRingBufferChunkAccess { public: class Chunk { public: class iterator { public: iterator(void* pStorage, size_t bytes); iterator(const iterator& rhs); iterator& operator=(const iterator& rhs); bool operator==(const iterator& rhs) const; pRingItemHeader operator->(); RingItemHeader& operator*(); iterator& operator++(); iterator operator++(int); }; Chunk(); void setChunk(size_t bytesInChunk, void* pStorage); void* getStorage(); size_t size() const; iterator begin(); iterator end(); }; public: CRingBufferChunkAccess(CRingBuffer* pRingBuffer); size_t waitChunk(size_t maxChunk, int polls = 0, int usecPoll = 0); Chunk nextChunk(); };
The CRingItem
class provides a convenient
method to create and commit ring items to a ring buffer as well
as to consumer ring items from a ring buffer. The downside of this
convenience is that data must be copied, sometimes several times.
This class provides consumers with zero copy access to ring items
in a ring buffer.
The model is that the user asks for, and eventually gets, a chunk of items. For the most part chunks represent a contiguous block of items in the ring buffer shared memory that have been produced but not yet consumed by this consumer. The user can then create an iterator which allows them to obtain individual items from the chunk.
The class handles the (rare for ring item sizes >> than the typical ring item) case where a ring item wraps by creating a single item chunk that's been copied from the ring buffer into a contiguous block of local memory.
Each invocation of nextChunk
releases the ring items the previous chunk contains allowing that
storage in the ring buffer to be re-used by the producer.
CRingBufferChunkAccess(CRingBuffer* pRingBuffer);
Constructs a zero copy ring item accessor.
pRingBuffer
is a pointer to a
CRingBuffer
object that must have been
created as a consumer. If this pointer does not represent
a current consumer to the ring buffer, a
std::logic_error
exception is thrown.
size_t waitChunk(size_t maxChunk, int polls = = 0, int usecPoll = = 0);
Blocks the caller until the appropriate condition is met.
The block ends when there's at least
maxChunk
bytes of data waiting to
be consumed by the client.
While blocking the client blocks for usecPoll
microseconds between each check of the number of bytes available.
If polls
checks have been done,
blocking ends.
Regardless of why or how this method returns, the return value is the number of bytes of data available for consumption. Note that this may, in fact be zero. NSCLDAQ ring items are inserted by our software atomically, which implies that if the return value is nonzero there will be at least one ring item available for consumption.
The defaults for polls
means the
method will return immediately. The default for
usecPoll
mean that the program will
spin wait. For true blocking, both should be non-zero, where
usecPoll
*polls
determines the maximum wait time and
usecPoll
the desired maximum latency for
ending the block.
CRingBufferChunkAccess::Chunk nextChunk();
Returns the next chunk as a
CRingBufferChunkAccess::Chunk
object.
This chunk is only valid until the next call to
nextChunk
. The size of the chunk
depends on ow much data is available at the time of the call
and where the top of the ring buffer is relative to the
get pointer for this client.
See CRingBufferChunkAccess::Chunk METHODS below for the methods defined on a chunk.
Chunk();
Constructs an empty chunk. Note that user code does not need
to call this, only the
CRingBufferChunkAccess
::nextChunk
method.
void setChunk(size_t bytesInChunk, void* pStorage);
Sets the pointer and size of the storage associated with a chunk
Again, this normally does not have to be called by user level
code,
CRingBufferChunkAccess
::nextChunk
will invoke this to initialize the chunk it hands back to the
user.
void* getStorage();
Returns the pointer to the chunk's data.
const size_t size();
Returns the number of bytes in the chunk.
CRingBufferChunkAccess::Chunk::iterator begin();
Returns an iterator to the first item in the chunk. See CRingBufferChunkAccess::Chunk::iterator METHODS below for the things you can do with an iterator.
Suffice to say that iterators behave like STL forward iterators.
CRingBufferChunkAccess::Chunk::iterator end();
Returns an end of iteration iterator. This iterator can be though of as pointing just off the end of the chunk.
Normally your code does not explicitly construct an iterator.
Instead, the CRingBufferChunkAccess::Chunk
you got from the ring buffer will be used to construct
iterators that allow you to iterate over the ring items in
that chunk.
iterator
(void* pStorage, size_t bytes);
Constructs an iterator of ring items on an arbitrary
chunk of storage. Normally the
CRingBufferChunkAccess::Chunk
::begin
method uses this.
iterator(const iterator& rhs);
Copy constructor for iterators.
iterator& operator=(const iterator& rhs);
Supports assignment of iterators.
const bool operator==(const iterator& rhs);
Supports comparison of two iterators for equality.
pRingItemHeader operator->();
Supports field dereferincing an iterator as if it were a pointer to a Ring item header e.g. given an iterator p, p->s_type is the ring item type.
RingItemHeader& operator*();
Supports dereferencing an iterator as if it were a pointer to a ring item header. For example, given an iterator p, reinterpret_cast<pRingItem>(&(*p)) produces a pointer to the ring item as a whole.
iterator& operator++();
Pre increments an iterator. That is the iterator is incremnted and a reference to the resulting incremented iterator is returned. This is the most efficient increment.
iterator operator++(int );
Supports post incrementing an iterator. A copy of the iterator is produced, the iterator is incremented and a copy of the iterator prior to the increment is returned. In general, this requires two copies of the iterator.