spsc_pbuf: second large write fails with -ENOMEM after buffer is emptied due to non-normalized internal indices

After a full consume of an spsc_pbuf, the buffer can become logically empty while rd_idx and wr_idx both remain at a non-zero offset. This breaks the next large allocation and causes spsc_pbuf_write() to return -ENOMEM.

Environment:

  • nRF Connect SDK v3.2.4
  • Zephyr OS v4.2.99-9673eec75908
  • Hardware: nRF9160

Problem:

After a successful spsc_pbuf_write() and spsc_pbuf_read() that empties the buffer, a second write of the same size fails with -ENOMEM even though the buffer is logically empty and has enough total capacity.

Steps to reproduce:

  1. Initialize an spsc_pbuf with 4096 bytes.
  2. spsc_pbuf_write() a 2511-byte packet — succeeds.
  3. spsc_pbuf_read() the packet — succeeds, buffer is empty.
  4. spsc_pbuf_write() another 2511-byte packet — fails with -ENOMEM.

Root cause analysis:

A 2511-byte packet uses:

  • 2511 bytes payload
  • 2-byte length header
  • padding to 4-byte alignment
  • total internal advance = 2516 bytes

After the read, rd_idx == wr_idx == 2516. The buffer is empty, but the indices are not reset to 0, so the next allocation sees a non-canonical empty buffer state and fails to allocate the new packet.

Proposed fix:

In spsc_pbuf_free(), after advancing rd_idx, if the buffer is empty (rd_idx == wr_idx), normalize the empty state by resetting the index(es) to 0.

Workaround:

Reinitialize the buffer after each successful read:

int ret = spsc_pbuf_read(json_cmd_pbuf, dst, dst_len); 

if (ret > 0) { 
    json_cmd_pbuf = spsc_pbuf_init(json_cmd_pbuf_mem, sizeof(json_cmd_pbuf_mem), 0); 
}

Related