Context: Real-time embedded ARM microcontroller.
Writing a lock-free, wait-free SPSC ring buffer is straight forward if the data in the buffer can be read or written atomically. But, in many cases, the buffer needs to be read over many ops, or written through many ops. How can a SPSC lock-free ring buffer be implemented? (The goal is for the ring-buffer to overwrite oldest data on overflow, not block.)
Imagine that the read of entry N is in process. Now, the write pointer catches up to N. If the read of N hadn't started, we'd simply overwrite N, and advance the read ptr to N+1; N becomes the last entry (most recently written) and N+1 the first entry (least recently written). But, now, since the read is in progress, we don't want to overwrite N. Instead, we need to make the write ptr N + 1 and the read ptr N + 2.
Likewise, if the write ptr is at N, and the read ptr advances to N-1. If the write to N-1 is complete, we can read N-1. But what if the write is in progress.
It seems like in addition to write ptr and read ptr, we also need to know if write is in progress and read is in progress. However, using those two extra information to construct invariants that keep the ring buffer correct is very difficult.
Questions:
For a lock-free, wait-free, SPSC ring buffer with overflow, that does not allow reading an item until write is complete, and does not allow overwriting an item that is in progress of reading:
- What data do we need (besides read ptr and write ptr)?
- What invariants apply to that data?
- How do we use that to construct the buffer?