1/* @title: Single-Producer Single-Consumer Lock-Free FIFO */
2#pragma once
3#include <stdatomic.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7
8struct spsc_fifo {
9 _Atomic size_t head; /* Written by Producer */
10 _Atomic size_t tail; /* Written by Consumer */
11 size_t size; /* Capacity (pow2) */
12 size_t mask; /* size - 1 */
13 uint8_t *data;
14};
15
16#define SPSC_FIFO_INIT \
17 (struct spsc_fifo) { \
18 .head = 0, .tail = 0, .size = 0, .mask = 0, .data = NULL \
19 }
20
21/* (rounded to power of 2) */
22bool spsc_fifo_init(struct spsc_fifo *fifo, size_t size);
23
24void spsc_fifo_init_with(struct spsc_fifo *fifo, void *buffer, size_t size);
25
26void spsc_fifo_destroy(struct spsc_fifo *fifo);
27
28size_t spsc_fifo_write(struct spsc_fifo *fifo, const void *src, size_t len);
29
30size_t spsc_fifo_read(struct spsc_fifo *fifo, void *dst, size_t len);
31
32/* peek up to `len` bytes into `dst` without consuming */
33size_t spsc_fifo_peek(const struct spsc_fifo *fifo, void *dst, size_t len);
34
35bool spsc_fifo_push_ptr(struct spsc_fifo *fifo, const void *ptr);
36bool spsc_fifo_pop_ptr(struct spsc_fifo *fifo, void **out_ptr);
37
38static inline size_t spsc_fifo_len(const struct spsc_fifo *fifo) {
39 size_t h = atomic_load_explicit(&fifo->head, memory_order_acquire);
40 size_t t = atomic_load_explicit(&fifo->tail, memory_order_relaxed);
41 return h - t;
42}
43
44static inline size_t spsc_fifo_avail(const struct spsc_fifo *fifo) {
45 return fifo->size - spsc_fifo_len(fifo);
46}
47
48static inline bool spsc_fifo_is_empty(const struct spsc_fifo *fifo) {
49 return spsc_fifo_len(fifo) == 0;
50}
51
52static inline bool spsc_fifo_is_full(const struct spsc_fifo *fifo) {
53 return spsc_fifo_avail(fifo) == 0;
54}
55
56static inline void spsc_fifo_reset(struct spsc_fifo *fifo) {
57 atomic_store_explicit(&fifo->head, 0, memory_order_relaxed);
58 atomic_store_explicit(&fifo->tail, 0, memory_order_relaxed);
59}
60