Description
Why: The class has a member variable: _numElems
Which is updated by both of these methods.
For example
template <int N>
int RingBufferN<N>::read_char()
{
if (isEmpty())
return -1;
uint8_t value = _aucBuffer[_iTail];
_iTail = nextIndex(_iTail);
_numElems--;
return value;
}
unless _numElems-- is atomic, there is the risk that in between the fetch and store, an interrupt happens which calls store_char one or more times, which each of these calls does numElems++. The store operation will then miss these other changes.
Context: - I am currently experimenting with changes the the SerialX objects for the Uno R4 code base, and was seeing that the processing of interrupts either to store away a character received into one Ring Buffer or to fetch the next byte from the write ring buffer to output, was taking more time than I was expecting. More of this in the issue:
arduino/ArduinoCore-renesas#75
Possible solution: In the past I and I know many others have written buffer classes that the store_char method, would only update the _iHead member variable, and the read_char only updates the _iTail member, and when necessary, you compute how many elements are in the buffer, like:
template <int N>
int SaferRingBufferN<N>::available()
{
if (_iHead >= _iTail)
return _iHead - _iTail;
return N + _iHead - _iTail;
}
Which is safer. It does not solve the multiple producers or consumers problem.
For those cases you might need something like the SafeRingBuffer code that is in that core. Although I have modified version, which only protects one side. either the Reads or the Writes, where the other side has only the single consumer or producer of the interrupt.
I am testing a version of this now, which appears to be faster.
Should the API version be updated?