Zephyr API Documentation 4.0.0
A Scalable Open Source RTOS
Loading...
Searching...
No Matches
spsc_lockfree.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#ifndef ZEPHYR_SYS_SPSC_LOCKFREE_H_
8#define ZEPHYR_SYS_SPSC_LOCKFREE_H_
9
10#include <stdint.h>
11#include <stdbool.h>
13#include <zephyr/sys/atomic.h>
15
59struct spsc {
60 /* private value only the producer thread should mutate */
61 unsigned long acquire;
62
63 /* private value only the consumer thread should mutate */
64 unsigned long consume;
65
66 /* producer mutable, consumer readable */
67 atomic_t in;
68
69 /* consumer mutable, producer readable */
70 atomic_t out;
71
72 /* mask used to automatically wrap values */
73 const unsigned long mask;
74};
75
82#define SPSC_INITIALIZER(sz, buf) \
83 { \
84 ._spsc = \
85 { \
86 .acquire = 0, \
87 .consume = 0, \
88 .in = ATOMIC_INIT(0), \
89 .out = ATOMIC_INIT(0), \
90 .mask = sz - 1, \
91 }, \
92 .buffer = buf, \
93 }
94
101#define SPSC_DECLARE(name, type) \
102 static struct spsc_##name { \
103 struct spsc _spsc; \
104 type * const buffer; \
105 }
106
114#define SPSC_DEFINE(name, type, sz) \
115 BUILD_ASSERT(IS_POWER_OF_TWO(sz)); \
116 static type __spsc_buf_##name[sz]; \
117 SPSC_DECLARE(name, type) name = SPSC_INITIALIZER(sz, __spsc_buf_##name);
118
124#define spsc_size(spsc) ((spsc)->_spsc.mask + 1)
125
133#define z_spsc_mask(spsc, i) ((i) & (spsc)->_spsc.mask)
134
139#define z_spsc_in(spsc) (unsigned long)atomic_get(&(spsc)->_spsc.in)
140
145#define z_spsc_out(spsc) (unsigned long)atomic_get(&(spsc)->_spsc.out)
146
155#define spsc_reset(spsc) \
156 ({ \
157 (spsc)->_spsc.consume = 0; \
158 (spsc)->_spsc.acquire = 0; \
159 atomic_set(&(spsc)->_spsc.in, 0); \
160 atomic_set(&(spsc)->_spsc.out, 0); \
161 })
162
170#define spsc_acquire(spsc) \
171 ({ \
172 unsigned long idx = z_spsc_in(spsc) + (spsc)->_spsc.acquire; \
173 bool spsc_acq = (idx - z_spsc_out(spsc)) < spsc_size(spsc); \
174 if (spsc_acq) { \
175 (spsc)->_spsc.acquire += 1; \
176 } \
177 spsc_acq ? &((spsc)->buffer[z_spsc_mask(spsc, idx)]) : NULL; \
178 })
179
187#define spsc_produce(spsc) \
188 ({ \
189 if ((spsc)->_spsc.acquire > 0) { \
190 (spsc)->_spsc.acquire -= 1; \
191 atomic_add(&(spsc)->_spsc.in, 1); \
192 } \
193 })
194
203#define spsc_produce_all(spsc) \
204 ({ \
205 if ((spsc)->_spsc.acquire > 0) { \
206 unsigned long acquired = (spsc)->_spsc.acquire; \
207 (spsc)->_spsc.acquire = 0; \
208 atomic_add(&(spsc)->_spsc.in, acquired); \
209 } \
210 })
211
219#define spsc_drop_all(spsc) \
220 do { \
221 (spsc)->_spsc.acquire = 0; \
222 } while (false)
223
231#define spsc_consume(spsc) \
232 ({ \
233 unsigned long idx = z_spsc_out(spsc) + (spsc)->_spsc.consume; \
234 bool has_consumable = (idx != z_spsc_in(spsc)); \
235 if (has_consumable) { \
236 (spsc)->_spsc.consume += 1; \
237 } \
238 has_consumable ? &((spsc)->buffer[z_spsc_mask(spsc, idx)]) : NULL; \
239 })
240
246#define spsc_release(spsc) \
247 ({ \
248 if ((spsc)->_spsc.consume > 0) { \
249 (spsc)->_spsc.consume -= 1; \
250 atomic_add(&(spsc)->_spsc.out, 1); \
251 } \
252 })
253
259#define spsc_release_all(spsc) \
260 ({ \
261 if ((spsc)->_spsc.consume > 0) { \
262 unsigned long consumed = (spsc)->_spsc.consume; \
263 (spsc)->_spsc.consume = 0; \
264 atomic_add(&(spsc)->_spsc.out, consumed); \
265 } \
266 })
267
273#define spsc_acquirable(spsc) \
274 ({ (((spsc)->_spsc.in + (spsc)->_spsc.acquire) - (spsc)->_spsc.out) - spsc_size(spsc); })
275
281#define spsc_consumable(spsc) ({ (spsc)->_spsc.in - (spsc)->_spsc.out - (spsc)->_spsc.consume; })
282
290#define spsc_peek(spsc) \
291 ({ \
292 unsigned long idx = z_spsc_out(spsc) + (spsc)->_spsc.consume; \
293 bool has_consumable = (idx != z_spsc_in(spsc)); \
294 has_consumable ? &((spsc)->buffer[z_spsc_mask(spsc, idx)]) : NULL; \
295 })
296
306#define spsc_next(spsc, item) \
307 ({ \
308 unsigned long idx = ((item) - (spsc)->buffer); \
309 bool has_next = \
310 z_spsc_mask(spsc, (idx + 1)) != (z_spsc_mask(spsc, z_spsc_in(spsc))); \
311 has_next ? &((spsc)->buffer[z_spsc_mask((spsc), idx + 1)]) : NULL; \
312 })
313
322#define spsc_prev(spsc, item) \
323 ({ \
324 unsigned long idx = ((item) - &(spsc)->buffer[0]) / sizeof((spsc)->buffer[0]); \
325 bool has_prev = idx != z_spsc_mask(spsc, z_spsc_out(spsc)); \
326 has_prev ? &((spsc)->buffer[z_spsc_mask(spsc, idx - 1)]) : NULL; \
327 })
328
333#endif /* ZEPHYR_SYS_SPSC_LOCKFREE_H_ */
long atomic_t
Definition atomic_types.h:15
Common toolchain abstraction.
Macro utilities.