1 // SPDX-FileCopyrightText: 2010-2012 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com> 2 // SPDX-FileCopyrightText: 2011-2012 Lai Jiangshan <laijs (at) cn.fujitsu.com> 3 // 4 // SPDX-License-Identifier: LGPL-2.1-or-later 5 6 #ifndef _URCU_WFCQUEUE_STATIC_H 7 #define _URCU_WFCQUEUE_STATIC_H 8 9 /* 10 * Userspace RCU library - Concurrent Queue with Wait-Free Enqueue/Blocking Dequeue 11 * 12 * TO BE INCLUDED ONLY IN LGPL-COMPATIBLE CODE. See urcu/wfcqueue.h for 13 * linking dynamically with the userspace rcu library. 14 */ 15 16 #include <pthread.h> 17 #include <poll.h> 18 #include <stdbool.h> 19 #include <urcu/assert.h> 20 #include <urcu/compiler.h> 21 #include <urcu/uatomic.h> 22 23 #ifdef __cplusplus 24 extern "C" { 25 #endif 26 27 /* 28 * Concurrent queue with wait-free enqueue/blocking dequeue. 29 * 30 * This queue has been designed and implemented collaboratively by 31 * Mathieu Desnoyers and Lai Jiangshan. Inspired from 32 * half-wait-free/half-blocking queue implementation done by Paul E. 33 * McKenney. 34 * 35 * Mutual exclusion of cds_wfcq_* / __cds_wfcq_* API 36 * 37 * Synchronization table: 38 * 39 * External synchronization techniques described in the API below is 40 * required between pairs marked with "X". No external synchronization 41 * required between pairs marked with "-". 42 * 43 * Legend: 44 * [1] cds_wfcq_enqueue 45 * [2] __cds_wfcq_splice (destination queue) 46 * [3] __cds_wfcq_dequeue 47 * [4] __cds_wfcq_splice (source queue) 48 * [5] __cds_wfcq_first 49 * [6] __cds_wfcq_next 50 * 51 * [1] [2] [3] [4] [5] [6] 52 * [1] - - - - - - 53 * [2] - - - - - - 54 * [3] - - X X X X 55 * [4] - - X - X X 56 * [5] - - X X - - 57 * [6] - - X X - - 58 * 59 * Mutual exclusion can be ensured by holding cds_wfcq_dequeue_lock(). 60 * 61 * For convenience, cds_wfcq_dequeue_blocking() and 62 * cds_wfcq_splice_blocking() hold the dequeue lock. 63 * 64 * Besides locking, mutual exclusion of dequeue, splice and iteration 65 * can be ensured by performing all of those operations from a single 66 * thread, without requiring any lock. 67 */ 68 69 #define WFCQ_ADAPT_ATTEMPTS 10 /* Retry if being set */ 70 #define WFCQ_WAIT 10 /* Wait 10 ms if being set */ 71 72 /* 73 * cds_wfcq_node_init: initialize wait-free queue node. 74 */ 75 static inline void _cds_wfcq_node_init(struct cds_wfcq_node *node) 76 { 77 node->next = NULL; 78 } 79 80 static inline void _cds_wfcq_node_init_atomic(struct cds_wfcq_node *node) 81 { 82 uatomic_store(&node->next, NULL, CMM_RELAXED); 83 } 84 85 /* 86 * cds_wfcq_init: initialize wait-free queue (with lock). Pair with 87 * cds_wfcq_destroy(). 88 */ 89 static inline void _cds_wfcq_init(struct cds_wfcq_head *head, 90 struct cds_wfcq_tail *tail) 91 { 92 int ret; 93 94 /* Set queue head and tail */ 95 _cds_wfcq_node_init(&head->node); 96 tail->p = &head->node; 97 ret = pthread_mutex_init(&head->lock, NULL); 98 urcu_posix_assert(!ret); 99 } 100 101 /* 102 * cds_wfcq_destroy: destroy wait-free queue (with lock). Pair with 103 * cds_wfcq_init(). 104 */ 105 static inline void _cds_wfcq_destroy(struct cds_wfcq_head *head, 106 struct cds_wfcq_tail *tail __attribute__((unused))) 107 { 108 int ret = pthread_mutex_destroy(&head->lock); 109 urcu_posix_assert(!ret); 110 } 111 112 /* 113 * __cds_wfcq_init: initialize wait-free queue (without lock). Don't 114 * pair with any destroy function. 115 */ 116 static inline void ___cds_wfcq_init(struct __cds_wfcq_head *head, 117 struct cds_wfcq_tail *tail) 118 { 119 /* Set queue head and tail */ 120 _cds_wfcq_node_init(&head->node); 121 tail->p = &head->node; 122 } 123 124 /* 125 * cds_wfcq_empty: return whether wait-free queue is empty. 126 * 127 * No memory barrier is issued. No mutual exclusion is required. 128 * 129 * We perform the test on head->node.next to check if the queue is 130 * possibly empty, but we confirm this by checking if the tail pointer 131 * points to the head node because the tail pointer is the linearisation 132 * point of the enqueuers. Just checking the head next pointer could 133 * make a queue appear empty if an enqueuer is preempted for a long time 134 * between xchg() and setting the previous node's next pointer. 135 */ 136 static inline bool _cds_wfcq_empty(cds_wfcq_head_const_ptr_t u_head, 137 const struct cds_wfcq_tail *tail) 138 { 139 const struct __cds_wfcq_head *head = u_head._h; 140 /* 141 * Queue is empty if no node is pointed by head->node.next nor 142 * tail->p. Even though the tail->p check is sufficient to find 143 * out of the queue is empty, we first check head->node.next as a 144 * common case to ensure that dequeuers do not frequently access 145 * enqueuer's tail->p cache line. 146 */ 147 return uatomic_load(&head->node.next, CMM_CONSUME) == NULL 148 && uatomic_load(&tail->p, CMM_CONSUME) == &head->node; 149 } 150 151 static inline void _cds_wfcq_dequeue_lock(struct cds_wfcq_head *head, 152 struct cds_wfcq_tail *tail __attribute__((unused))) 153 { 154 int ret; 155 156 ret = pthread_mutex_lock(&head->lock); 157 urcu_posix_assert(!ret); 158 } 159 160 static inline void _cds_wfcq_dequeue_unlock(struct cds_wfcq_head *head, 161 struct cds_wfcq_tail *tail __attribute__((unused))) 162 { 163 int ret; 164 165 ret = pthread_mutex_unlock(&head->lock); 166 urcu_posix_assert(!ret); 167 } 168 169 static inline bool ___cds_wfcq_append(cds_wfcq_head_ptr_t u_head, 170 struct cds_wfcq_tail *tail, 171 struct cds_wfcq_node *new_head, 172 struct cds_wfcq_node *new_tail) 173 { 174 struct __cds_wfcq_head *head = u_head._h; 175 struct cds_wfcq_node *old_tail; 176 177 /* 178 * Implicit memory barrier before uatomic_xchg() orders earlier 179 * stores to data structure containing node and setting 180 * node->next to NULL before publication. 181 */ 182 old_tail = uatomic_xchg_mo(&tail->p, new_tail, CMM_SEQ_CST); 183 184 /* 185 * Implicit memory barrier after uatomic_xchg() orders store to 186 * q->tail before store to old_tail->next. 187 * 188 * At this point, dequeuers see a NULL tail->p->next, which 189 * indicates that the queue is being appended to. The following 190 * store will append "node" to the queue from a dequeuer 191 * perspective. 192 */ 193 uatomic_store(&old_tail->next, new_head, CMM_RELEASE); 194 195 /* 196 * Return false if queue was empty prior to adding the node, 197 * else return true. 198 */ 199 return old_tail != &head->node; 200 } 201 202 /* 203 * cds_wfcq_enqueue: enqueue a node into a wait-free queue. 204 * 205 * Operations prior to enqueue are consistant with respect to dequeuing or 206 * splicing and iterating. 207 * 208 * Returns false if the queue was empty prior to adding the node. 209 * Returns true otherwise. 210 */ 211 static inline bool _cds_wfcq_enqueue(cds_wfcq_head_ptr_t head, 212 struct cds_wfcq_tail *tail, 213 struct cds_wfcq_node *new_tail) 214 { 215 cmm_emit_legacy_smp_mb(); 216 217 return ___cds_wfcq_append(head, tail, new_tail, new_tail); 218 } 219 220 /* 221 * CDS_WFCQ_WAIT_SLEEP: 222 * 223 * By default, this sleeps for the given @msec milliseconds. 224 * This is a macro which LGPL users may #define themselves before 225 * including wfcqueue.h to override the default behavior (e.g. 226 * to log a warning or perform other background work). 227 */ 228 #ifndef CDS_WFCQ_WAIT_SLEEP 229 #define CDS_WFCQ_WAIT_SLEEP(msec) ___cds_wfcq_wait_sleep(msec) 230 #endif 231 232 static inline void ___cds_wfcq_wait_sleep(int msec) 233 { 234 (void) poll(NULL, 0, msec); 235 } 236 237 /* 238 * ___cds_wfcq_busy_wait: adaptative busy-wait. 239 * 240 * Returns 1 if nonblocking and needs to block, 0 otherwise. 241 */ 242 static inline bool 243 ___cds_wfcq_busy_wait(int *attempt, int blocking) 244 { 245 if (!blocking) 246 return 1; 247 if (++(*attempt) >= WFCQ_ADAPT_ATTEMPTS) { 248 CDS_WFCQ_WAIT_SLEEP(WFCQ_WAIT); /* Wait for 10ms */ 249 *attempt = 0; 250 } else { 251 caa_cpu_relax(); 252 } 253 return 0; 254 } 255 256 /* 257 * Waiting for enqueuer to complete enqueue and return the next node. 258 */ 259 static inline struct cds_wfcq_node * 260 ___cds_wfcq_node_sync_next(struct cds_wfcq_node *node, int blocking) 261 { 262 struct cds_wfcq_node *next; 263 int attempt = 0; 264 265 /* 266 * Adaptative busy-looping waiting for enqueuer to complete enqueue. 267 * 268 * Load node.next before loading node's content 269 */ 270 while ((next = uatomic_load(&node->next, CMM_CONSUME)) == NULL) { 271 if (___cds_wfcq_busy_wait(&attempt, blocking)) 272 return CDS_WFCQ_WOULDBLOCK; 273 } 274 275 return next; 276 } 277 278 static inline struct cds_wfcq_node * 279 ___cds_wfcq_first(cds_wfcq_head_ptr_t u_head, 280 struct cds_wfcq_tail *tail, 281 int blocking) 282 { 283 struct __cds_wfcq_head *head = u_head._h; 284 struct cds_wfcq_node *node; 285 286 if (_cds_wfcq_empty(__cds_wfcq_head_const_cast(head), tail)) 287 return NULL; 288 node = ___cds_wfcq_node_sync_next(&head->node, blocking); 289 290 return node; 291 } 292 293 /* 294 * __cds_wfcq_first_blocking: get first node of a queue, without dequeuing. 295 * 296 * Content written into the node before enqueue is guaranteed to be 297 * consistent, but no other memory ordering is ensured. 298 * Dequeue/splice/iteration mutual exclusion should be ensured by the 299 * caller. 300 * 301 * Used by for-like iteration macros in urcu/wfqueue.h: 302 * __cds_wfcq_for_each_blocking() 303 * __cds_wfcq_for_each_blocking_safe() 304 * 305 * Returns NULL if queue is empty, first node otherwise. 306 */ 307 static inline struct cds_wfcq_node * 308 ___cds_wfcq_first_blocking(cds_wfcq_head_ptr_t head, 309 struct cds_wfcq_tail *tail) 310 { 311 return ___cds_wfcq_first(head, tail, 1); 312 } 313 314 315 /* 316 * __cds_wfcq_first_nonblocking: get first node of a queue, without dequeuing. 317 * 318 * Same as __cds_wfcq_first_blocking, but returns CDS_WFCQ_WOULDBLOCK if 319 * it needs to block. 320 */ 321 static inline struct cds_wfcq_node * 322 ___cds_wfcq_first_nonblocking(cds_wfcq_head_ptr_t head, 323 struct cds_wfcq_tail *tail) 324 { 325 return ___cds_wfcq_first(head, tail, 0); 326 } 327 328 static inline struct cds_wfcq_node * 329 ___cds_wfcq_next(cds_wfcq_head_ptr_t head __attribute__((unused)), 330 struct cds_wfcq_tail *tail, 331 struct cds_wfcq_node *node, 332 int blocking) 333 { 334 struct cds_wfcq_node *next; 335 336 /* 337 * Even though the following tail->p check is sufficient to find 338 * out if we reached the end of the queue, we first check 339 * node->next as a common case to ensure that iteration on nodes 340 * do not frequently access enqueuer's tail->p cache line. 341 * 342 * Load node->next before loading next's content 343 */ 344 if ((next = uatomic_load(&node->next, CMM_CONSUME)) == NULL) { 345 if (uatomic_load(&tail->p, CMM_RELAXED) == node) 346 return NULL; 347 next = ___cds_wfcq_node_sync_next(node, blocking); 348 } 349 350 return next; 351 } 352 353 /* 354 * __cds_wfcq_next_blocking: get next node of a queue, without dequeuing. 355 * 356 * Content written into the node before enqueue is guaranteed to be 357 * consistent, but no other memory ordering is ensured. 358 * Dequeue/splice/iteration mutual exclusion should be ensured by the 359 * caller. 360 * 361 * Used by for-like iteration macros in urcu/wfqueue.h: 362 * __cds_wfcq_for_each_blocking() 363 * __cds_wfcq_for_each_blocking_safe() 364 * 365 * Returns NULL if reached end of queue, non-NULL next queue node 366 * otherwise. 367 */ 368 static inline struct cds_wfcq_node * 369 ___cds_wfcq_next_blocking(cds_wfcq_head_ptr_t head, 370 struct cds_wfcq_tail *tail, 371 struct cds_wfcq_node *node) 372 { 373 return ___cds_wfcq_next(head, tail, node, 1); 374 } 375 376 /* 377 * __cds_wfcq_next_blocking: get next node of a queue, without dequeuing. 378 * 379 * Same as __cds_wfcq_next_blocking, but returns CDS_WFCQ_WOULDBLOCK if 380 * it needs to block. 381 */ 382 static inline struct cds_wfcq_node * 383 ___cds_wfcq_next_nonblocking(cds_wfcq_head_ptr_t head, 384 struct cds_wfcq_tail *tail, 385 struct cds_wfcq_node *node) 386 { 387 return ___cds_wfcq_next(head, tail, node, 0); 388 } 389 390 static inline struct cds_wfcq_node * 391 ___cds_wfcq_dequeue_with_state(cds_wfcq_head_ptr_t u_head, 392 struct cds_wfcq_tail *tail, 393 int *state, 394 int blocking) 395 { 396 struct __cds_wfcq_head *head = u_head._h; 397 struct cds_wfcq_node *node, *next; 398 399 if (state) 400 *state = 0; 401 402 if (_cds_wfcq_empty(__cds_wfcq_head_const_cast(head), tail)) { 403 return NULL; 404 } 405 406 node = ___cds_wfcq_node_sync_next(&head->node, blocking); 407 if (!blocking && node == CDS_WFCQ_WOULDBLOCK) { 408 return CDS_WFCQ_WOULDBLOCK; 409 } 410 411 if ((next = uatomic_load(&node->next, CMM_CONSUME)) == NULL) { 412 /* 413 * @node is probably the only node in the queue. 414 * Try to move the tail to &q->head. 415 * q->head.next is set to NULL here, and stays 416 * NULL if the cmpxchg succeeds. Should the 417 * cmpxchg fail due to a concurrent enqueue, the 418 * q->head.next will be set to the next node. 419 */ 420 _cds_wfcq_node_init_atomic(&head->node); 421 if (uatomic_cmpxchg_mo(&tail->p, node, &head->node, 422 CMM_SEQ_CST, CMM_SEQ_CST) == node) { 423 if (state) 424 *state |= CDS_WFCQ_STATE_LAST; 425 cmm_emit_legacy_smp_mb(); 426 return node; 427 } 428 next = ___cds_wfcq_node_sync_next(node, blocking); 429 /* 430 * In nonblocking mode, if we would need to block to 431 * get node's next, set the head next node pointer 432 * (currently NULL) back to its original value. 433 */ 434 if (!blocking && next == CDS_WFCQ_WOULDBLOCK) { 435 uatomic_store(&head->node.next, node, CMM_RELAXED); 436 return CDS_WFCQ_WOULDBLOCK; 437 } 438 } 439 440 /* 441 * Move queue head forward. 442 */ 443 uatomic_store(&head->node.next, next, CMM_RELAXED); 444 cmm_emit_legacy_smp_mb(); 445 446 return node; 447 } 448 449 /* 450 * __cds_wfcq_dequeue_with_state_blocking: dequeue node from queue, with state. 451 * 452 * Content written into the node before enqueue is guaranteed to be 453 * consistent, but no other memory ordering is ensured. 454 * It is valid to reuse and free a dequeued node immediately. 455 * Dequeue/splice/iteration mutual exclusion should be ensured by the 456 * caller. 457 */ 458 static inline struct cds_wfcq_node * 459 ___cds_wfcq_dequeue_with_state_blocking(cds_wfcq_head_ptr_t head, 460 struct cds_wfcq_tail *tail, int *state) 461 { 462 return ___cds_wfcq_dequeue_with_state(head, tail, state, 1); 463 } 464 465 /* 466 * ___cds_wfcq_dequeue_blocking: dequeue node from queue. 467 * 468 * Same as __cds_wfcq_dequeue_with_state_blocking, but without saving 469 * state. 470 */ 471 static inline struct cds_wfcq_node * 472 ___cds_wfcq_dequeue_blocking(cds_wfcq_head_ptr_t head, 473 struct cds_wfcq_tail *tail) 474 { 475 return ___cds_wfcq_dequeue_with_state_blocking(head, tail, NULL); 476 } 477 478 /* 479 * __cds_wfcq_dequeue_with_state_nonblocking: dequeue node, with state. 480 * 481 * Same as __cds_wfcq_dequeue_blocking, but returns CDS_WFCQ_WOULDBLOCK 482 * if it needs to block. 483 */ 484 static inline struct cds_wfcq_node * 485 ___cds_wfcq_dequeue_with_state_nonblocking(cds_wfcq_head_ptr_t head, 486 struct cds_wfcq_tail *tail, int *state) 487 { 488 return ___cds_wfcq_dequeue_with_state(head, tail, state, 0); 489 } 490 491 /* 492 * ___cds_wfcq_dequeue_nonblocking: dequeue node from queue. 493 * 494 * Same as __cds_wfcq_dequeue_with_state_nonblocking, but without saving 495 * state. 496 */ 497 static inline struct cds_wfcq_node * 498 ___cds_wfcq_dequeue_nonblocking(cds_wfcq_head_ptr_t head, 499 struct cds_wfcq_tail *tail) 500 { 501 return ___cds_wfcq_dequeue_with_state_nonblocking(head, tail, NULL); 502 } 503 504 /* 505 * __cds_wfcq_splice: enqueue all src_q nodes at the end of dest_q. 506 * 507 * Operations after splice are consistant with respect to enqueue. 508 * 509 * Dequeue all nodes from src_q. 510 * dest_q must be already initialized. 511 * Mutual exclusion for src_q should be ensured by the caller as 512 * specified in the "Synchronisation table". 513 * Returns enum cds_wfcq_ret which indicates the state of the src or 514 * dest queue. 515 */ 516 static inline enum cds_wfcq_ret 517 ___cds_wfcq_splice( 518 cds_wfcq_head_ptr_t u_dest_q_head, 519 struct cds_wfcq_tail *dest_q_tail, 520 cds_wfcq_head_ptr_t u_src_q_head, 521 struct cds_wfcq_tail *src_q_tail, 522 int blocking) 523 { 524 struct __cds_wfcq_head *dest_q_head = u_dest_q_head._h; 525 struct __cds_wfcq_head *src_q_head = u_src_q_head._h; 526 struct cds_wfcq_node *head, *tail; 527 int attempt = 0; 528 529 /* 530 * Initial emptiness check to speed up cases where queue is 531 * empty: only require loads to check if queue is empty. 532 */ 533 if (_cds_wfcq_empty(__cds_wfcq_head_const_cast(src_q_head), src_q_tail)) 534 return CDS_WFCQ_RET_SRC_EMPTY; 535 536 for (;;) { 537 /* 538 * Open-coded _cds_wfcq_empty() by testing result of 539 * uatomic_xchg, as well as tail pointer vs head node 540 * address. 541 */ 542 head = uatomic_xchg_mo(&src_q_head->node.next, NULL, CMM_SEQ_CST); 543 if (head) 544 break; /* non-empty */ 545 if (uatomic_load(&src_q_tail->p, CMM_CONSUME) == &src_q_head->node) 546 return CDS_WFCQ_RET_SRC_EMPTY; 547 if (___cds_wfcq_busy_wait(&attempt, blocking)) 548 return CDS_WFCQ_RET_WOULDBLOCK; 549 } 550 551 /* 552 * Memory barrier implied before uatomic_xchg() orders store to 553 * src_q->head before store to src_q->tail. This is required by 554 * concurrent enqueue on src_q, which exchanges the tail before 555 * updating the previous tail's next pointer. 556 */ 557 cmm_emit_legacy_smp_mb(); 558 tail = uatomic_xchg_mo(&src_q_tail->p, &src_q_head->node, CMM_SEQ_CST); 559 560 /* 561 * Append the spliced content of src_q into dest_q. Does not 562 * require mutual exclusion on dest_q (wait-free). 563 */ 564 if (___cds_wfcq_append(__cds_wfcq_head_cast(dest_q_head), dest_q_tail, 565 head, tail)) 566 return CDS_WFCQ_RET_DEST_NON_EMPTY; 567 else 568 return CDS_WFCQ_RET_DEST_EMPTY; 569 } 570 571 /* 572 * __cds_wfcq_splice_blocking: enqueue all src_q nodes at the end of dest_q. 573 * 574 * Dequeue all nodes from src_q. 575 * dest_q must be already initialized. 576 * Mutual exclusion for src_q should be ensured by the caller as 577 * specified in the "Synchronisation table". 578 * Returns enum cds_wfcq_ret which indicates the state of the src or 579 * dest queue. Never returns CDS_WFCQ_RET_WOULDBLOCK. 580 */ 581 static inline enum cds_wfcq_ret 582 ___cds_wfcq_splice_blocking( 583 cds_wfcq_head_ptr_t dest_q_head, 584 struct cds_wfcq_tail *dest_q_tail, 585 cds_wfcq_head_ptr_t src_q_head, 586 struct cds_wfcq_tail *src_q_tail) 587 { 588 return ___cds_wfcq_splice(dest_q_head, dest_q_tail, 589 src_q_head, src_q_tail, 1); 590 } 591 592 /* 593 * __cds_wfcq_splice_nonblocking: enqueue all src_q nodes at the end of dest_q. 594 * 595 * Same as __cds_wfcq_splice_blocking, but returns 596 * CDS_WFCQ_RET_WOULDBLOCK if it needs to block. 597 */ 598 static inline enum cds_wfcq_ret 599 ___cds_wfcq_splice_nonblocking( 600 cds_wfcq_head_ptr_t dest_q_head, 601 struct cds_wfcq_tail *dest_q_tail, 602 cds_wfcq_head_ptr_t src_q_head, 603 struct cds_wfcq_tail *src_q_tail) 604 { 605 return ___cds_wfcq_splice(dest_q_head, dest_q_tail, 606 src_q_head, src_q_tail, 0); 607 } 608 609 /* 610 * cds_wfcq_dequeue_with_state_blocking: dequeue a node from a wait-free queue. 611 * 612 * Content written into the node before enqueue is guaranteed to be 613 * consistent, but no other memory ordering is ensured. 614 * Mutual exclusion with cds_wfcq_splice_blocking and dequeue lock is 615 * ensured. 616 * It is valid to reuse and free a dequeued node immediately. 617 */ 618 static inline struct cds_wfcq_node * 619 _cds_wfcq_dequeue_with_state_blocking(struct cds_wfcq_head *head, 620 struct cds_wfcq_tail *tail, int *state) 621 { 622 struct cds_wfcq_node *retval; 623 624 _cds_wfcq_dequeue_lock(head, tail); 625 retval = ___cds_wfcq_dequeue_with_state_blocking(cds_wfcq_head_cast(head), 626 tail, state); 627 _cds_wfcq_dequeue_unlock(head, tail); 628 return retval; 629 } 630 631 /* 632 * cds_wfcq_dequeue_blocking: dequeue node from queue. 633 * 634 * Same as cds_wfcq_dequeue_blocking, but without saving state. 635 */ 636 static inline struct cds_wfcq_node * 637 _cds_wfcq_dequeue_blocking(struct cds_wfcq_head *head, 638 struct cds_wfcq_tail *tail) 639 { 640 return _cds_wfcq_dequeue_with_state_blocking(head, tail, NULL); 641 } 642 643 /* 644 * cds_wfcq_splice_blocking: enqueue all src_q nodes at the end of dest_q. 645 * 646 * Dequeue all nodes from src_q. 647 * dest_q must be already initialized. 648 * Content written into the node before enqueue is guaranteed to be 649 * consistent, but no other memory ordering is ensured. 650 * Mutual exclusion with cds_wfcq_dequeue_blocking and dequeue lock is 651 * ensured. 652 * Returns enum cds_wfcq_ret which indicates the state of the src or 653 * dest queue. Never returns CDS_WFCQ_RET_WOULDBLOCK. 654 */ 655 static inline enum cds_wfcq_ret 656 _cds_wfcq_splice_blocking( 657 struct cds_wfcq_head *dest_q_head, 658 struct cds_wfcq_tail *dest_q_tail, 659 struct cds_wfcq_head *src_q_head, 660 struct cds_wfcq_tail *src_q_tail) 661 { 662 enum cds_wfcq_ret ret; 663 664 _cds_wfcq_dequeue_lock(src_q_head, src_q_tail); 665 ret = ___cds_wfcq_splice_blocking(cds_wfcq_head_cast(dest_q_head), dest_q_tail, 666 cds_wfcq_head_cast(src_q_head), src_q_tail); 667 _cds_wfcq_dequeue_unlock(src_q_head, src_q_tail); 668 return ret; 669 } 670 671 #ifdef __cplusplus 672 } 673 #endif 674 675 #endif /* _URCU_WFCQUEUE_STATIC_H */ 676