Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
spsc_ring.c File Reference
Include dependency graph for spsc_ring.c:

Go to the source code of this file.

Macros

#define MIN_MAG   2
#define MAX_MAG   MAX_POW2_EXP_FOR_UNSIGNED(SRN_SPSC_RING_CAP_TYPE)

Functions

srn_spsc_ring_tsrn_spsc_ring_make (srn_context_t *ctx, size_t magnitude, size_t elem_size)
 Set up a ring by allocating the buf to be the size of cap * elem_size.
bool srn_spsc_ring_push (srn_spsc_ring_t *r, const void *elem)
 Producer side.
bool srn_spsc_ring_pop (srn_spsc_ring_t *r, void *out)
 Consumer side.
bool srn_spsc_ring_empty (const srn_spsc_ring_t *r)
 Whether the ring currently holds no elements.

Macro Definition Documentation

◆ MAX_MAG

Definition at line 25 of file spsc_ring.c.

◆ MIN_MAG

#define MIN_MAG   2

Definition at line 24 of file spsc_ring.c.

Function Documentation

◆ srn_spsc_ring_empty()

bool srn_spsc_ring_empty ( const srn_spsc_ring_t * r)

Whether the ring currently holds no elements.

For the consumer this is exact; for any other observer it is a hint.

Definition at line 103 of file spsc_ring.c.

103 {
104 // Exact for the consumer, the only party that moves `head`. For any other
105 // observer it is a hint, since the producer may publish right after the load.
106 return atomic_load_explicit(&r->head, memory_order_acquire) ==
107 atomic_load_explicit(&r->tail, memory_order_acquire);
108}
_Atomic size_t tail
Producer index. Only the producer advances it.
Definition spsc_ring.h:52
_Atomic size_t head
Consumer index. Only the consumer advances it.
Definition spsc_ring.h:49
Here is the caller graph for this function:

◆ srn_spsc_ring_make()

srn_spsc_ring_t * srn_spsc_ring_make ( srn_context_t * ctx,
size_t magnitude,
size_t elem_size )

Set up a ring by allocating the buf to be the size of cap * elem_size.

And it will choose the cap to be 2 ^ magnitude.

Definition at line 27 of file spsc_ring.c.

27 {
28 PANIC_IF_NULL(ctx);
29
30 // Capacity is `2 ^ magnitude`, so it is always a power of two and a slot
31 // index can be masked rather than divided. The magnitude must stay in range:
32 // below MIN_MAG the ring is too small to be useful, and above MAX_MAG the
33 // shift no longer fits `SRN_SPSC_RING_CAP_TYPE`. An out-of-range request
34 // fails as a value carrying an error rather than aborting.
35 if ((magnitude < MIN_MAG) || (magnitude > MAX_MAG)) {
36 return FAIL(ctx, srn_spsc_ring_t, WRONG_RANGE, "Magnitude is not in the supported range.");
37 }
38
40
41 // A fresh ring is empty -- head and tail both start at zero -- and carries no
42 // error. `mask` is `cap - 1`, the low bits that select a slot, and `buf`
43 // holds `cap` slots of `elem_size` bytes each.
44 atomic_init(&r->head, 0);
45 atomic_init(&r->tail, 0);
46
47 r->maybe_error = nullptr;
48 r->cap = ((SRN_SPSC_RING_CAP_TYPE)1U << magnitude);
49 r->mask = r->cap - 1;
50 r->elem_size = elem_size;
51 // The buffer multiply must not wrap, a wrapped size would allocate a tiny
52 // buffer while push/pop still index up to `cap` slots.
53 PANIC_IF(elem_size == 0, "'elem_size' must be nonzero");
54 PANIC_IF(r->cap > SIZE_MAX / elem_size, "ring buffer size overflows");
55 r->buf = ALLOCN(ctx, unsigned char, (r->cap * elem_size));
57
58 return r;
59}
#define ALLOCN(ctx, T, N)
Definition context.h:85
#define ALLOC(ctx, T)
Definition context.h:84
#define FAIL(ctx, T, err, msg)
Definition errors.h:149
@ WRONG_RANGE
Definition errors.h:84
#define MAX_MAG
Definition spsc_ring.c:25
#define MIN_MAG
Definition spsc_ring.c:24
#define SRN_SPSC_RING_CAP_TYPE
Definition spsc_ring.h:41
size_t elem_size
Definition spsc_ring.h:56
SRN_SPSC_RING_CAP_TYPE cap
Definition spsc_ring.h:54
SRN_SPSC_RING_CAP_TYPE mask
Definition spsc_ring.h:55
unsigned char * buf
cap * elem_size bytes.
Definition spsc_ring.h:60
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59
Here is the caller graph for this function:

◆ srn_spsc_ring_pop()

bool srn_spsc_ring_pop ( srn_spsc_ring_t * r,
void * out )

Consumer side.

Copy one element into *out. Returns false when the ring is empty, leaving *out untouched.

Definition at line 83 of file spsc_ring.c.

83 {
84 // Mirror of push. The consumer alone advances `head` (relaxed), and reads the
85 // producer's `tail` with acquire so it sees every element published so far.
86 const size_t h = atomic_load_explicit(&r->head, memory_order_relaxed);
87 const size_t t = atomic_load_explicit(&r->tail, memory_order_acquire);
88
89 // Equal indices mean nothing new has been published since the last pop:
90 // empty.
91 if (h == t) {
92 return false;
93 }
94
95 // Copy the element out, then free the slot by advancing `head` with release.
96 // The release orders this read before a producer's acquire of `head`, so the
97 // producer never reuses the slot while it is still being read here.
98 memcpy(out, r->buf + ((h & r->mask) * r->elem_size), r->elem_size);
99 atomic_store_explicit(&r->head, h + 1, memory_order_release);
100 return true;
101}
Here is the caller graph for this function:

◆ srn_spsc_ring_push()

bool srn_spsc_ring_push ( srn_spsc_ring_t * r,
const void * elem )

Producer side.

Copy *elem into the ring. Returns false when the ring is full, leaving it unchanged.

Definition at line 61 of file spsc_ring.c.

61 {
62 // The producer alone advances `tail`, so reading it needs no synchronisation.
63 // `head` belongs to the consumer; the acquire load makes the slots it has
64 // freed visible here.
65 const size_t t = atomic_load_explicit(&r->tail, memory_order_relaxed);
66 const size_t h = atomic_load_explicit(&r->head, memory_order_acquire);
67
68 // `t - h` is the live count, correct across the unsigned wrap of the
69 // counters. At `cap` the ring is full; refuse rather than overwrite a slot
70 // the consumer has not read yet.
71 if (t - h >= r->cap) {
72 return false;
73 }
74
75 // Write the element, then publish it by advancing `tail` with release. A
76 // consumer that later acquires this `tail` value is guaranteed to see the
77 // bytes written just above, never a half-filled slot.
78 memcpy(r->buf + ((t & r->mask) * r->elem_size), elem, r->elem_size);
79 atomic_store_explicit(&r->tail, t + 1, memory_order_release);
80 return true;
81}
Here is the caller graph for this function: