A lock-free circular recorder of the raw process image, one record per RT cycle.
More...
|
| | ProcessDataRing ()=default |
| |
| | ~ProcessDataRing () |
| |
| | ProcessDataRing (const ProcessDataRing &)=delete |
| |
| ProcessDataRing & | operator= (const ProcessDataRing &)=delete |
| |
| void | allocate (uint32_t inputCap, uint32_t outputCap, size_t capacityCycles) |
| | (Re)allocates the ring for an image of inputCap / outputCap bytes per direction and capacity cycles, discarding any prior recording (sequence restarts at 0).
|
| |
| void | clear () |
| | Releases the storage and resets to the unallocated state. Idempotent.
|
| |
| bool | allocated () const |
| | Whether allocate has been called with a non-zero capacity.
|
| |
| void | write (uint64_t timestampNs, int wkc, std::span< const uint8_t > inputs, std::span< const uint8_t > outputs) |
| | Records one cycle. RT, wait-free, single-writer. No-op until allocate.
|
| |
| uint64_t | head () const |
| | The producer's next sequence number; head()-1 is the newest recorded cycle, and head()==0 means nothing has been recorded yet.
|
| |
| size_t | capacity () const |
| | Number of cycles the ring can hold.
|
| |
| uint64_t | oldestValidSeq () const |
| | The oldest sequence number still in the ring: max(0, head - capacity). A cursor below this has been lapped (its data overwritten) and must resync to this value.
|
| |
| bool | readRecord (uint64_t seq, Record &out) const |
| | Copies the record for seq into out. Lock-free; retries are the caller's job.
|
| |
A lock-free circular recorder of the raw process image, one record per RT cycle.
The RT loop appends one record every cycle via write() and never blocks: a record is the whole raw input and output IOmap for that cycle plus its timestamp and working counter. The ring overwrites its oldest record on wrap, so it holds the most recent capacity() cycles — a flight-data recorder, not a drain queue. Any number of non-RT readers consume it through independent read cursors (a read index, not a tail): a reader never frees a slot and never gates the producer, so the RT loop overwrites unconditionally and may lap a slow reader. That is allowed and detected, not a bug — a lapped reader sees readRecord return false and resyncs from oldestValidSeq().
It is the single RT-written source for both the live monitoring stream (each monitoring holds a cursor and ships every record in [cursor, head)) and point reads of the freshest value (read head()-1) — one write per cycle, and one record holds both directions from the same cycle.
Sequencing: each record carries an absolute cycle sequence number; slot = seq % capacity. head() is the producer's monotonic next-seq (it never wraps). The per-slot sequence word is stored after the payload with release ordering — its publication is what makes a record readable — and a reader re-checks it after copying to detect a write that raced the copy (a per-slot sequence re-check). The RT write() is wait-free: a few memcpy plus two release stores.
Single writer (the RT loop), many concurrent readers. Not copyable or movable.