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