**Approach (brief)** Use an atomic pointer to the buffer plus an internal atomic reference count inside the buffer. Readers atomically load pointer, increment its refcount, then re-check pointer stability; writer allocates new buffer, atomically swaps pointer, then decrements the old buffer's refcount and frees if it reaches zero. Use acquire/release ordering to ensure visibility and avoid data races.**Code sketch (C, embedded-friendly)**c
#include <stdatomic.h>
#include <stdlib.h>
typedef struct Buffer {
atomic_int ref; // reference count
size_t len;
uint8_t data[]; // flexible array
} Buffer;
atomic_uintptr_t g_buf_ptr = ATOMIC_VAR_INIT((uintptr_t)NULL);
// reader: returns pointer to buffer (caller must release)
Buffer* buf_acquire(void) {
for (;;) {
uintptr_t p = atomic_load_explicit(&g_buf_ptr, memory_order_acquire);
Buffer* b = (Buffer*)p;
if (!b) return NULL;
// increase refcount
atomic_fetch_add_explicit(&b->ref, 1, memory_order_acq_rel);
// verify pointer unchanged
if ((uintptr_t)b == atomic_load_explicit(&g_buf_ptr, memory_order_acquire))
return b;
// pointer changed: drop our ref and retry
if (atomic_fetch_sub_explicit(&b->ref, 1, memory_order_acq_rel) == 1) {
// last ref; safe to reclaim
// ensure prior writes are visible before free
atomic_thread_fence(memory_order_acquire);
free(b);
}
}
}
void buf_release(Buffer* b) {
if (!b) return;
if (atomic_fetch_sub_explicit(&b->ref, 1, memory_order_acq_rel) == 1) {
atomic_thread_fence(memory_order_acquire);
free(b);
}
}
// writer replaces buffer atomically
void buf_replace(Buffer* newb) {
// newb should have initial ref == 1 for writer ownership
uintptr_t oldp = atomic_exchange_explicit(&g_buf_ptr, (uintptr_t)newb, memory_order_acq_rel);
Buffer* oldb = (Buffer*)oldp;
if (oldb) {
if (atomic_fetch_sub_explicit(&oldb->ref, 1, memory_order_acq_rel) == 1) {
atomic_thread_fence(memory_order_acquire);
free(oldb);
}
}
}
**Key atomic operations and memory ordering**- atomic_load(memory_order_acquire) when reading pointer: ensures subsequent reads of buffer data see initialized content.- atomic_fetch_add/sub(memory_order_acq_rel): increment/decrement refcount with combined acquire-release semantics to synchronize readers/writer.- atomic_exchange(memory_order_acq_rel) for swap to publish new buffer and get old pointer.- atomic_thread_fence(memory_order_acquire) before free ensures prior releases are observed.**Safety & corner cases**- ABA: if pointer can flip between same addresses (rare if buffers are freshly malloc'd). For embedded with limited allocator reuse, include a version/tag in pointer (combine pointer+counter) to avoid ABA.- Reader preemption between load and increment: solved by increasing refcount after load and verifying pointer unchanged; if changed, decrement and retry.- Reclamation: only free when refcount transitions to zero; the last releaser uses acquire fence before free to ensure safe reclamation.- Single writer simplifies (no concurrent writer-writer ABA), but if multiple writers exist, use CAS loop or a pointer+version and ensure writer increments newb->ref appropriately.**Why this is correct**- Readers hold a ref while accessing data; writer frees only when ref==0 so no reader can be accessing memory being freed.- Acquire/release pairs ensure proper visibility ordering between writer initialization of buffer and readers seeing that data.- The re-check after increment prevents the race where pointer changed after the reader incremented a stale buffer.This pattern is compact, lock-free for readers, and suitable for resource-constrained embedded systems.