Commit | Line | Data |
---|---|---|
d3d3857f MJ |
1 | // SPDX-FileCopyrightText: 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
2 | // | |
3 | // SPDX-License-Identifier: LGPL-2.1-or-later | |
4 | ||
cb3f3d6b MD |
5 | #ifndef _URCU_WFSTACK_H |
6 | #define _URCU_WFSTACK_H | |
7 | ||
8 | /* | |
edac6b69 | 9 | * Userspace RCU library - Stack with wait-free push, blocking traversal. |
cb3f3d6b MD |
10 | */ |
11 | ||
12 | #include <pthread.h> | |
edac6b69 | 13 | #include <stdbool.h> |
cb3f3d6b MD |
14 | #include <urcu/compiler.h> |
15 | ||
16 | #ifdef __cplusplus | |
17 | extern "C" { | |
18 | #endif | |
19 | ||
edac6b69 MD |
20 | /* |
21 | * Stack with wait-free push, blocking traversal. | |
22 | * | |
23 | * Stack implementing push, pop, pop_all operations, as well as iterator | |
24 | * on the stack head returned by pop_all. | |
25 | * | |
c97c6ce5 MD |
26 | * Wait-free operations: cds_wfs_push, __cds_wfs_pop_all, cds_wfs_empty, |
27 | * cds_wfs_first. | |
28 | * Blocking operations: cds_wfs_pop, cds_wfs_pop_all, cds_wfs_next, | |
29 | * iteration on stack head returned by pop_all. | |
edac6b69 MD |
30 | * |
31 | * Synchronization table: | |
32 | * | |
33 | * External synchronization techniques described in the API below is | |
34 | * required between pairs marked with "X". No external synchronization | |
35 | * required between pairs marked with "-". | |
36 | * | |
37 | * cds_wfs_push __cds_wfs_pop __cds_wfs_pop_all | |
38 | * cds_wfs_push - - - | |
39 | * __cds_wfs_pop - X X | |
40 | * __cds_wfs_pop_all - X - | |
41 | * | |
42 | * cds_wfs_pop and cds_wfs_pop_all use an internal mutex to provide | |
43 | * synchronization. | |
44 | */ | |
45 | ||
28757437 | 46 | #define CDS_WFS_WOULDBLOCK ((struct cds_wfs_node *) -1UL) |
af67624d | 47 | |
c8975b94 MD |
48 | enum cds_wfs_state { |
49 | CDS_WFS_STATE_LAST = (1U << 0), | |
50 | }; | |
51 | ||
edac6b69 MD |
52 | /* |
53 | * struct cds_wfs_node is returned by __cds_wfs_pop, and also used as | |
54 | * iterator on stack. It is not safe to dereference the node next | |
55 | * pointer when returned by __cds_wfs_pop_blocking. | |
56 | */ | |
16aa9ee8 DG |
57 | struct cds_wfs_node { |
58 | struct cds_wfs_node *next; | |
cb3f3d6b MD |
59 | }; |
60 | ||
edac6b69 MD |
61 | /* |
62 | * struct cds_wfs_head is returned by __cds_wfs_pop_all, and can be used | |
63 | * to begin iteration on the stack. "node" needs to be the first field of | |
64 | * cds_wfs_head, so the end-of-stack pointer value can be used for both | |
65 | * types. | |
66 | */ | |
67 | struct cds_wfs_head { | |
68 | struct cds_wfs_node node; | |
69 | }; | |
70 | ||
718eb63e EW |
71 | struct __cds_wfs_stack { |
72 | struct cds_wfs_head *head; | |
73 | }; | |
74 | ||
16aa9ee8 | 75 | struct cds_wfs_stack { |
edac6b69 | 76 | struct cds_wfs_head *head; |
cb3f3d6b MD |
77 | pthread_mutex_t lock; |
78 | }; | |
79 | ||
718eb63e | 80 | /* |
3ad920b4 | 81 | * In C, the transparent union allows calling functions that work on both |
718eb63e EW |
82 | * struct cds_wfs_stack and struct __cds_wfs_stack on any of those two |
83 | * types. | |
28757437 | 84 | * |
3ad920b4 MD |
85 | * In C++, implement static inline wrappers using function overloading |
86 | * to obtain an API similar to C. | |
087bce43 MD |
87 | * |
88 | * Avoid complaints from clang++ not knowing the transparent union | |
89 | * attribute. | |
718eb63e | 90 | */ |
087bce43 MD |
91 | #if defined(__clang__) |
92 | #pragma clang diagnostic push | |
93 | #pragma clang diagnostic ignored "-Wignored-attributes" | |
94 | #endif | |
5135c0fd | 95 | typedef union { |
718eb63e EW |
96 | struct __cds_wfs_stack *_s; |
97 | struct cds_wfs_stack *s; | |
087bce43 | 98 | } __attribute__((__transparent_union__)) cds_wfs_stack_ptr_t; |
c4a5a2ff MD |
99 | |
100 | typedef union { | |
101 | const struct __cds_wfs_stack *_s; | |
102 | const struct cds_wfs_stack *s; | |
103 | } __attribute__((__transparent_union__)) cds_wfs_stack_const_ptr_t; | |
087bce43 MD |
104 | #if defined(__clang__) |
105 | #pragma clang diagnostic pop | |
106 | #endif | |
718eb63e | 107 | |
294d3396 | 108 | #ifdef _LGPL_SOURCE |
cb3f3d6b | 109 | |
af7c2dbe | 110 | #include <urcu/static/wfstack.h> |
cb3f3d6b | 111 | |
16aa9ee8 | 112 | #define cds_wfs_node_init _cds_wfs_node_init |
edac6b69 | 113 | #define cds_wfs_init _cds_wfs_init |
200d100e | 114 | #define cds_wfs_destroy _cds_wfs_destroy |
711ff0f9 | 115 | #define __cds_wfs_init ___cds_wfs_init |
edac6b69 MD |
116 | #define cds_wfs_empty _cds_wfs_empty |
117 | #define cds_wfs_push _cds_wfs_push | |
118 | ||
119 | /* Locking performed internally */ | |
120 | #define cds_wfs_pop_blocking _cds_wfs_pop_blocking | |
c8975b94 | 121 | #define cds_wfs_pop_with_state_blocking _cds_wfs_pop_with_state_blocking |
edac6b69 MD |
122 | #define cds_wfs_pop_all_blocking _cds_wfs_pop_all_blocking |
123 | ||
124 | /* | |
125 | * For iteration on cds_wfs_head returned by __cds_wfs_pop_all or | |
126 | * cds_wfs_pop_all_blocking. | |
127 | */ | |
c7ba06ba | 128 | #define cds_wfs_first _cds_wfs_first |
edac6b69 | 129 | #define cds_wfs_next_blocking _cds_wfs_next_blocking |
150fc1bb | 130 | #define cds_wfs_next_nonblocking _cds_wfs_next_nonblocking |
edac6b69 MD |
131 | |
132 | /* Pop locking with internal mutex */ | |
133 | #define cds_wfs_pop_lock _cds_wfs_pop_lock | |
134 | #define cds_wfs_pop_unlock _cds_wfs_pop_unlock | |
135 | ||
136 | /* Synchronization ensured by the caller. See synchronization table. */ | |
137 | #define __cds_wfs_pop_blocking ___cds_wfs_pop_blocking | |
c8975b94 MD |
138 | #define __cds_wfs_pop_with_state_blocking \ |
139 | ___cds_wfs_pop_with_state_blocking | |
150fc1bb | 140 | #define __cds_wfs_pop_nonblocking ___cds_wfs_pop_nonblocking |
c8975b94 MD |
141 | #define __cds_wfs_pop_with_state_nonblocking \ |
142 | ___cds_wfs_pop_with_state_nonblocking | |
edac6b69 | 143 | #define __cds_wfs_pop_all ___cds_wfs_pop_all |
cb3f3d6b | 144 | |
294d3396 | 145 | #else /* !_LGPL_SOURCE */ |
cb3f3d6b | 146 | |
edac6b69 MD |
147 | /* |
148 | * cds_wfs_node_init: initialize wait-free stack node. | |
149 | */ | |
16aa9ee8 | 150 | extern void cds_wfs_node_init(struct cds_wfs_node *node); |
edac6b69 MD |
151 | |
152 | /* | |
200d100e MD |
153 | * cds_wfs_init: initialize wait-free stack (with lock). Pair with |
154 | * cds_wfs_destroy(). | |
edac6b69 | 155 | */ |
16aa9ee8 | 156 | extern void cds_wfs_init(struct cds_wfs_stack *s); |
edac6b69 | 157 | |
718eb63e | 158 | /* |
200d100e MD |
159 | * cds_wfs_destroy: destroy wait-free stack (with lock). Pair with |
160 | * cds_wfs_init(). | |
161 | */ | |
162 | extern void cds_wfs_destroy(struct cds_wfs_stack *s); | |
163 | ||
164 | /* | |
165 | * __cds_wfs_init: initialize wait-free stack (no lock). Don't pair with | |
166 | * any destroy function. | |
718eb63e EW |
167 | */ |
168 | extern void __cds_wfs_init(struct __cds_wfs_stack *s); | |
169 | ||
edac6b69 MD |
170 | /* |
171 | * cds_wfs_empty: return whether wait-free stack is empty. | |
172 | * | |
173 | * No memory barrier is issued. No mutual exclusion is required. | |
174 | */ | |
c4a5a2ff | 175 | extern bool cds_wfs_empty(cds_wfs_stack_const_ptr_t u_stack); |
edac6b69 MD |
176 | |
177 | /* | |
178 | * cds_wfs_push: push a node into the stack. | |
179 | * | |
180 | * Issues a full memory barrier before push. No mutual exclusion is | |
181 | * required. | |
182 | * | |
183 | * Returns 0 if the stack was empty prior to adding the node. | |
184 | * Returns non-zero otherwise. | |
185 | */ | |
718eb63e | 186 | extern int cds_wfs_push(cds_wfs_stack_ptr_t u_stack, struct cds_wfs_node *node); |
edac6b69 MD |
187 | |
188 | /* | |
189 | * cds_wfs_pop_blocking: pop a node from the stack. | |
190 | * | |
191 | * Calls __cds_wfs_pop_blocking with an internal pop mutex held. | |
192 | */ | |
16aa9ee8 | 193 | extern struct cds_wfs_node *cds_wfs_pop_blocking(struct cds_wfs_stack *s); |
cb3f3d6b | 194 | |
c8975b94 MD |
195 | /* |
196 | * cds_wfs_pop_with_state_blocking: pop a node from the stack, with state. | |
197 | * | |
198 | * Same as cds_wfs_pop_blocking, but stores whether the stack was | |
199 | * empty into state (CDS_WFS_STATE_LAST). | |
200 | */ | |
201 | extern struct cds_wfs_node * | |
202 | cds_wfs_pop_with_state_blocking(struct cds_wfs_stack *s, int *state); | |
203 | ||
edac6b69 MD |
204 | /* |
205 | * cds_wfs_pop_all_blocking: pop all nodes from a stack. | |
206 | * | |
207 | * Calls __cds_wfs_pop_all with an internal pop mutex held. | |
208 | */ | |
209 | extern struct cds_wfs_head *cds_wfs_pop_all_blocking(struct cds_wfs_stack *s); | |
210 | ||
211 | /* | |
c7ba06ba | 212 | * cds_wfs_first: get first node of a popped stack. |
edac6b69 MD |
213 | * |
214 | * Content written into the node before enqueue is guaranteed to be | |
215 | * consistent, but no other memory ordering is ensured. | |
216 | * | |
217 | * Used by for-like iteration macros in urcu/wfstack.h: | |
218 | * cds_wfs_for_each_blocking() | |
219 | * cds_wfs_for_each_blocking_safe() | |
8af2956c MD |
220 | * |
221 | * Returns NULL if popped stack is empty, top stack node otherwise. | |
edac6b69 | 222 | */ |
c7ba06ba | 223 | extern struct cds_wfs_node *cds_wfs_first(struct cds_wfs_head *head); |
edac6b69 MD |
224 | |
225 | /* | |
226 | * cds_wfs_next_blocking: get next node of a popped stack. | |
227 | * | |
228 | * Content written into the node before enqueue is guaranteed to be | |
229 | * consistent, but no other memory ordering is ensured. | |
230 | * | |
231 | * Used by for-like iteration macros in urcu/wfstack.h: | |
232 | * cds_wfs_for_each_blocking() | |
233 | * cds_wfs_for_each_blocking_safe() | |
8af2956c MD |
234 | * |
235 | * Returns NULL if reached end of popped stack, non-NULL next stack | |
236 | * node otherwise. | |
edac6b69 MD |
237 | */ |
238 | extern struct cds_wfs_node *cds_wfs_next_blocking(struct cds_wfs_node *node); | |
239 | ||
af67624d MD |
240 | /* |
241 | * cds_wfs_next_nonblocking: get next node of a popped stack. | |
242 | * | |
243 | * Same as cds_wfs_next_blocking, but returns CDS_WFS_WOULDBLOCK if it | |
244 | * needs to block. | |
245 | */ | |
246 | extern struct cds_wfs_node *cds_wfs_next_nonblocking(struct cds_wfs_node *node); | |
247 | ||
edac6b69 MD |
248 | /* |
249 | * cds_wfs_pop_lock: lock stack pop-protection mutex. | |
250 | */ | |
251 | extern void cds_wfs_pop_lock(struct cds_wfs_stack *s); | |
252 | ||
253 | /* | |
254 | * cds_wfs_pop_unlock: unlock stack pop-protection mutex. | |
255 | */ | |
256 | extern void cds_wfs_pop_unlock(struct cds_wfs_stack *s); | |
257 | ||
258 | /* | |
259 | * __cds_wfs_pop_blocking: pop a node from the stack. | |
260 | * | |
261 | * Returns NULL if stack is empty. | |
262 | * | |
263 | * __cds_wfs_pop_blocking needs to be synchronized using one of the | |
264 | * following techniques: | |
265 | * | |
266 | * 1) Calling __cds_wfs_pop_blocking under rcu read lock critical | |
267 | * section. The caller must wait for a grace period to pass before | |
268 | * freeing the returned node or modifying the cds_wfs_node structure. | |
269 | * 2) Using mutual exclusion (e.g. mutexes) to protect | |
270 | * __cds_wfs_pop_blocking and __cds_wfs_pop_all callers. | |
271 | * 3) Ensuring that only ONE thread can call __cds_wfs_pop_blocking() | |
272 | * and __cds_wfs_pop_all(). (multi-provider/single-consumer scheme). | |
273 | */ | |
711ff0f9 | 274 | extern struct cds_wfs_node *__cds_wfs_pop_blocking(cds_wfs_stack_ptr_t u_stack); |
edac6b69 | 275 | |
c8975b94 MD |
276 | /* |
277 | * __cds_wfs_pop_with_state_blocking: pop a node from the stack, with state. | |
278 | * | |
279 | * Same as __cds_wfs_pop_blocking, but stores whether the stack was | |
280 | * empty into state (CDS_WFS_STATE_LAST). | |
281 | */ | |
282 | extern struct cds_wfs_node * | |
711ff0f9 MD |
283 | __cds_wfs_pop_with_state_blocking(cds_wfs_stack_ptr_t u_stack, |
284 | int *state); | |
c8975b94 | 285 | |
af67624d MD |
286 | /* |
287 | * __cds_wfs_pop_nonblocking: pop a node from the stack. | |
288 | * | |
289 | * Same as __cds_wfs_pop_blocking, but returns CDS_WFS_WOULDBLOCK if | |
290 | * it needs to block. | |
291 | */ | |
711ff0f9 | 292 | extern struct cds_wfs_node *__cds_wfs_pop_nonblocking(cds_wfs_stack_ptr_t u_stack); |
af67624d | 293 | |
c8975b94 MD |
294 | /* |
295 | * __cds_wfs_pop_with_state_nonblocking: pop a node from the stack, with state. | |
296 | * | |
297 | * Same as __cds_wfs_pop_nonblocking, but stores whether the stack was | |
298 | * empty into state (CDS_WFS_STATE_LAST). | |
299 | */ | |
300 | extern struct cds_wfs_node * | |
711ff0f9 | 301 | __cds_wfs_pop_with_state_nonblocking(cds_wfs_stack_ptr_t u_stack, |
c8975b94 MD |
302 | int *state); |
303 | ||
edac6b69 MD |
304 | /* |
305 | * __cds_wfs_pop_all: pop all nodes from a stack. | |
306 | * | |
307 | * __cds_wfs_pop_all does not require any synchronization with other | |
308 | * push, nor with other __cds_wfs_pop_all, but requires synchronization | |
309 | * matching the technique used to synchronize __cds_wfs_pop_blocking: | |
310 | * | |
311 | * 1) If __cds_wfs_pop_blocking is called under rcu read lock critical | |
312 | * section, both __cds_wfs_pop_blocking and cds_wfs_pop_all callers | |
313 | * must wait for a grace period to pass before freeing the returned | |
314 | * node or modifying the cds_wfs_node structure. However, no RCU | |
315 | * read-side critical section is needed around __cds_wfs_pop_all. | |
316 | * 2) Using mutual exclusion (e.g. mutexes) to protect | |
317 | * __cds_wfs_pop_blocking and __cds_wfs_pop_all callers. | |
318 | * 3) Ensuring that only ONE thread can call __cds_wfs_pop_blocking() | |
319 | * and __cds_wfs_pop_all(). (multi-provider/single-consumer scheme). | |
320 | */ | |
711ff0f9 | 321 | extern struct cds_wfs_head *__cds_wfs_pop_all(cds_wfs_stack_ptr_t u_stack); |
edac6b69 | 322 | |
294d3396 | 323 | #endif /* !_LGPL_SOURCE */ |
cb3f3d6b | 324 | |
edac6b69 MD |
325 | /* |
326 | * cds_wfs_for_each_blocking: Iterate over all nodes returned by | |
327 | * __cds_wfs_pop_all(). | |
328 | * @head: head of the queue (struct cds_wfs_head pointer). | |
329 | * @node: iterator (struct cds_wfs_node pointer). | |
330 | * | |
331 | * Content written into each node before enqueue is guaranteed to be | |
332 | * consistent, but no other memory ordering is ensured. | |
333 | */ | |
334 | #define cds_wfs_for_each_blocking(head, node) \ | |
c7ba06ba | 335 | for (node = cds_wfs_first(head); \ |
edac6b69 MD |
336 | node != NULL; \ |
337 | node = cds_wfs_next_blocking(node)) | |
338 | ||
339 | /* | |
340 | * cds_wfs_for_each_blocking_safe: Iterate over all nodes returned by | |
341 | * __cds_wfs_pop_all(). Safe against deletion. | |
342 | * @head: head of the queue (struct cds_wfs_head pointer). | |
343 | * @node: iterator (struct cds_wfs_node pointer). | |
344 | * @n: struct cds_wfs_node pointer holding the next pointer (used | |
345 | * internally). | |
346 | * | |
347 | * Content written into each node before enqueue is guaranteed to be | |
348 | * consistent, but no other memory ordering is ensured. | |
349 | */ | |
350 | #define cds_wfs_for_each_blocking_safe(head, node, n) \ | |
c7ba06ba | 351 | for (node = cds_wfs_first(head), \ |
edac6b69 MD |
352 | n = (node ? cds_wfs_next_blocking(node) : NULL); \ |
353 | node != NULL; \ | |
354 | node = n, n = (node ? cds_wfs_next_blocking(node) : NULL)) | |
355 | ||
3ad920b4 MD |
356 | #ifdef __cplusplus |
357 | } | |
358 | ||
359 | /* | |
360 | * In C++, implement static inline wrappers using function overloading | |
361 | * to obtain an API similar to C. | |
362 | */ | |
363 | ||
94d0ecca | 364 | static inline cds_wfs_stack_ptr_t cds_wfs_stack_cast(struct __cds_wfs_stack *s) |
3ad920b4 MD |
365 | { |
366 | cds_wfs_stack_ptr_t ret = { | |
367 | ._s = s, | |
368 | }; | |
369 | return ret; | |
370 | } | |
371 | ||
372 | static inline cds_wfs_stack_ptr_t cds_wfs_stack_cast(struct cds_wfs_stack *s) | |
373 | { | |
374 | cds_wfs_stack_ptr_t ret = { | |
375 | .s = s, | |
376 | }; | |
377 | return ret; | |
378 | } | |
379 | ||
c4a5a2ff MD |
380 | static inline cds_wfs_stack_const_ptr_t cds_wfs_stack_const_cast(const struct __cds_wfs_stack *s) |
381 | { | |
382 | cds_wfs_stack_const_ptr_t ret = { | |
383 | ._s = s, | |
384 | }; | |
385 | return ret; | |
386 | } | |
387 | ||
388 | static inline cds_wfs_stack_const_ptr_t cds_wfs_stack_const_cast(const struct cds_wfs_stack *s) | |
389 | { | |
390 | cds_wfs_stack_const_ptr_t ret = { | |
391 | .s = s, | |
392 | }; | |
393 | return ret; | |
394 | } | |
395 | ||
c38fa027 | 396 | template<typename T> static inline bool cds_wfs_empty(T s) |
3ad920b4 | 397 | { |
c4a5a2ff | 398 | return cds_wfs_empty(cds_wfs_stack_const_cast(s)); |
3ad920b4 MD |
399 | } |
400 | ||
c38fa027 | 401 | template<typename T> static inline int cds_wfs_push(T s, struct cds_wfs_node *node) |
3ad920b4 MD |
402 | { |
403 | return cds_wfs_push(cds_wfs_stack_cast(s), node); | |
404 | } | |
405 | ||
c38fa027 | 406 | template<typename T> static inline struct cds_wfs_node *__cds_wfs_pop_blocking(T s) |
3ad920b4 | 407 | { |
94d0ecca | 408 | return __cds_wfs_pop_blocking(cds_wfs_stack_cast(s)); |
3ad920b4 MD |
409 | } |
410 | ||
c38fa027 MD |
411 | template<typename T> static inline struct cds_wfs_node * |
412 | __cds_wfs_pop_with_state_blocking(T s, int *state) | |
3ad920b4 MD |
413 | { |
414 | return __cds_wfs_pop_with_state_blocking(cds_wfs_stack_cast(s), state); | |
415 | } | |
416 | ||
c38fa027 | 417 | template<typename T> static inline struct cds_wfs_node *__cds_wfs_pop_nonblocking(T s) |
3ad920b4 MD |
418 | |
419 | { | |
94d0ecca | 420 | return __cds_wfs_pop_nonblocking(cds_wfs_stack_cast(s)); |
3ad920b4 MD |
421 | } |
422 | ||
c38fa027 MD |
423 | template<typename T> static inline struct cds_wfs_node * |
424 | __cds_wfs_pop_with_state_nonblocking(T s, int *state) | |
3ad920b4 MD |
425 | { |
426 | return __cds_wfs_pop_with_state_nonblocking(cds_wfs_stack_cast(s), state); | |
427 | } | |
428 | ||
c38fa027 | 429 | template<typename T> static inline struct cds_wfs_head *__cds_wfs_pop_all(T s) |
3ad920b4 MD |
430 | { |
431 | return __cds_wfs_pop_all(cds_wfs_stack_cast(s)); | |
432 | } | |
433 | ||
434 | #endif | |
435 | ||
cb3f3d6b | 436 | #endif /* _URCU_WFSTACK_H */ |