Home | History | Annotate | Line # | Download | only in urcu
      1 // SPDX-FileCopyrightText: 2010-2012 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com>
      2 //
      3 // SPDX-License-Identifier: LGPL-2.1-or-later
      4 
      5 #ifndef _URCU_WFSTACK_H
      6 #define _URCU_WFSTACK_H
      7 
      8 /*
      9  * Userspace RCU library - Stack with wait-free push, blocking traversal.
     10  */
     11 
     12 #include <pthread.h>
     13 #include <stdbool.h>
     14 #include <urcu/compiler.h>
     15 
     16 #ifdef __cplusplus
     17 extern "C" {
     18 #endif
     19 
     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  *
     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.
     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 
     46 #define CDS_WFS_WOULDBLOCK	((struct cds_wfs_node *) -1UL)
     47 
     48 enum cds_wfs_state {
     49 	CDS_WFS_STATE_LAST =		(1U << 0),
     50 };
     51 
     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  */
     57 struct cds_wfs_node {
     58 	struct cds_wfs_node *next;
     59 };
     60 
     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 
     71 struct __cds_wfs_stack {
     72 	struct cds_wfs_head *head;
     73 };
     74 
     75 struct cds_wfs_stack {
     76 	struct cds_wfs_head *head;
     77 	pthread_mutex_t lock;
     78 };
     79 
     80 /*
     81  * In C, the transparent union allows calling functions that work on both
     82  * struct cds_wfs_stack and struct __cds_wfs_stack on any of those two
     83  * types.
     84  *
     85  * In C++, implement static inline wrappers using function overloading
     86  * to obtain an API similar to C.
     87  *
     88  * Avoid complaints from clang++ not knowing the transparent union
     89  * attribute.
     90  */
     91 #if defined(__clang__)
     92 #pragma clang diagnostic push
     93 #pragma clang diagnostic ignored "-Wignored-attributes"
     94 #endif
     95 typedef union {
     96 	struct __cds_wfs_stack *_s;
     97 	struct cds_wfs_stack *s;
     98 } __attribute__((__transparent_union__)) cds_wfs_stack_ptr_t;
     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;
    104 #if defined(__clang__)
    105 #pragma clang diagnostic pop
    106 #endif
    107 
    108 #ifdef _LGPL_SOURCE
    109 
    110 #include <urcu/static/wfstack.h>
    111 
    112 #define cds_wfs_node_init		_cds_wfs_node_init
    113 #define cds_wfs_init			_cds_wfs_init
    114 #define cds_wfs_destroy			_cds_wfs_destroy
    115 #define __cds_wfs_init			___cds_wfs_init
    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
    121 #define cds_wfs_pop_with_state_blocking	_cds_wfs_pop_with_state_blocking
    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  */
    128 #define cds_wfs_first			_cds_wfs_first
    129 #define cds_wfs_next_blocking		_cds_wfs_next_blocking
    130 #define cds_wfs_next_nonblocking	_cds_wfs_next_nonblocking
    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
    138 #define __cds_wfs_pop_with_state_blocking	\
    139 					___cds_wfs_pop_with_state_blocking
    140 #define __cds_wfs_pop_nonblocking	___cds_wfs_pop_nonblocking
    141 #define __cds_wfs_pop_with_state_nonblocking	\
    142 					___cds_wfs_pop_with_state_nonblocking
    143 #define __cds_wfs_pop_all		___cds_wfs_pop_all
    144 
    145 #else /* !_LGPL_SOURCE */
    146 
    147 /*
    148  * cds_wfs_node_init: initialize wait-free stack node.
    149  */
    150 extern void cds_wfs_node_init(struct cds_wfs_node *node);
    151 
    152 /*
    153  * cds_wfs_init: initialize wait-free stack (with lock). Pair with
    154  * cds_wfs_destroy().
    155  */
    156 extern void cds_wfs_init(struct cds_wfs_stack *s);
    157 
    158 /*
    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.
    167  */
    168 extern void __cds_wfs_init(struct __cds_wfs_stack *s);
    169 
    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  */
    175 extern bool cds_wfs_empty(cds_wfs_stack_const_ptr_t u_stack);
    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  */
    186 extern int cds_wfs_push(cds_wfs_stack_ptr_t u_stack, struct cds_wfs_node *node);
    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  */
    193 extern struct cds_wfs_node *cds_wfs_pop_blocking(struct cds_wfs_stack *s);
    194 
    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 
    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 /*
    212  * cds_wfs_first: get first node of a popped stack.
    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()
    220  *
    221  * Returns NULL if popped stack is empty, top stack node otherwise.
    222  */
    223 extern struct cds_wfs_node *cds_wfs_first(struct cds_wfs_head *head);
    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()
    234  *
    235  * Returns NULL if reached end of popped stack, non-NULL next stack
    236  * node otherwise.
    237  */
    238 extern struct cds_wfs_node *cds_wfs_next_blocking(struct cds_wfs_node *node);
    239 
    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 
    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  */
    274 extern struct cds_wfs_node *__cds_wfs_pop_blocking(cds_wfs_stack_ptr_t u_stack);
    275 
    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 *
    283 	__cds_wfs_pop_with_state_blocking(cds_wfs_stack_ptr_t u_stack,
    284 		int *state);
    285 
    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  */
    292 extern struct cds_wfs_node *__cds_wfs_pop_nonblocking(cds_wfs_stack_ptr_t u_stack);
    293 
    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 *
    301 	__cds_wfs_pop_with_state_nonblocking(cds_wfs_stack_ptr_t u_stack,
    302 		int *state);
    303 
    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  */
    321 extern struct cds_wfs_head *__cds_wfs_pop_all(cds_wfs_stack_ptr_t u_stack);
    322 
    323 #endif /* !_LGPL_SOURCE */
    324 
    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)			\
    335 	for (node = cds_wfs_first(head);			\
    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)			   \
    351 	for (node = cds_wfs_first(head),				   \
    352 			n = (node ? cds_wfs_next_blocking(node) : NULL);   \
    353 		node != NULL;						   \
    354 		node = n, n = (node ? cds_wfs_next_blocking(node) : NULL))
    355 
    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 
    364 static inline cds_wfs_stack_ptr_t cds_wfs_stack_cast(struct __cds_wfs_stack *s)
    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 
    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 
    396 template<typename T> static inline bool cds_wfs_empty(T s)
    397 {
    398 	return cds_wfs_empty(cds_wfs_stack_const_cast(s));
    399 }
    400 
    401 template<typename T> static inline int cds_wfs_push(T s, struct cds_wfs_node *node)
    402 {
    403 	return cds_wfs_push(cds_wfs_stack_cast(s), node);
    404 }
    405 
    406 template<typename T> static inline struct cds_wfs_node *__cds_wfs_pop_blocking(T s)
    407 {
    408 	return __cds_wfs_pop_blocking(cds_wfs_stack_cast(s));
    409 }
    410 
    411 template<typename T> static inline struct cds_wfs_node *
    412 	__cds_wfs_pop_with_state_blocking(T s, int *state)
    413 {
    414 	return __cds_wfs_pop_with_state_blocking(cds_wfs_stack_cast(s), state);
    415 }
    416 
    417 template<typename T> static inline struct cds_wfs_node *__cds_wfs_pop_nonblocking(T s)
    418 
    419 {
    420 	return __cds_wfs_pop_nonblocking(cds_wfs_stack_cast(s));
    421 }
    422 
    423 template<typename T> static inline struct cds_wfs_node *
    424 	__cds_wfs_pop_with_state_nonblocking(T s, int *state)
    425 {
    426 	return __cds_wfs_pop_with_state_nonblocking(cds_wfs_stack_cast(s), state);
    427 }
    428 
    429 template<typename T> static inline struct cds_wfs_head *__cds_wfs_pop_all(T s)
    430 {
    431 	return __cds_wfs_pop_all(cds_wfs_stack_cast(s));
    432 }
    433 
    434 #endif
    435 
    436 #endif /* _URCU_WFSTACK_H */
    437