AndersKaloer/Ring-Buffer

Ring-Buffer not thread errrm interrupt-safe?

Closed this issue · 3 comments

Hello,

am I right, that the ring buffer is not interrupt-safe? I've had a problem at work, that it didn't work while receiving a lot of data (1 MB) and the ring buffer is configured for 2048 bytes. The main thing was, that data is received with UART non-blocking and while data is going to be written into the buffer in the callback function new data may be received already and read from the buffer in parallel.

bool ring_buffer_read(ring_buffer_t *buffer, uint8_t *data) {
  ring_buffer_size_t tail_index = buffer->tail_index;
  ring_buffer_size_t head_index = buffer->head_index;

  if (tail_index == head_index) {
    /* No items */
    return false;
  }
  
  *data = buffer->buffer[tail_index];
  buffer->tail_index = ((tail_index + 1) & RING_BUFFER_MASK(buffer));
  return true;
}

void ring_buffer_write(ring_buffer_t *buffer, uint8_t data) {
  ring_buffer_size_t tail_index = buffer->tail_index;
  ring_buffer_size_t head_index = buffer->head_index;

  /* Is buffer full? */
  if (((head_index - tail_index) & RING_BUFFER_MASK(buffer)) == RING_BUFFER_MASK(buffer)) {
    return;
  }

  /* Place data in buffer */
  buffer->buffer[head_index] = data;
  buffer->head_index = ((head_index + 1) & RING_BUFFER_MASK(buffer));
}

I've changed a few minor things, but regarding the interrup safety, I think storing first in a local variable is key (also seen in a talk in the bare metal series on YouTube).

Hi Johannes,

It is correct that the ring buffer is not thread/interrupt safe.

If you do bare-metal programming, you need to ensure that the ring buffer function calls are uninterrupted by functions that also access the ring buffer. The easiest way is to just disable all interrupts while you access the ring buffer. If you use an RTOS, you can protect the ring buffer with a mutex.

The use of local variables cannot guarantee interrupt/thread safety. Even if they could, they could be removed by the compiler.

I hope it helps.

Good point, in my tests using local variables always worked, but it might be compiler dependant. Probably it's better to disable interrupts in the start of the callback function, which is called, once a read is complete (line idle interrupt) before reading into the buffer and enable interrupts again at the end of the function.

Yes, that sounds like the right way to address your problem.