4 * Userspace RCU library - Red-Black Tree
6 * Copyright (c) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * Implementation of RCU-adapted data structures and operations based on the RB
23 * tree algorithms found in chapter 12 of:
25 * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and
26 * Clifford Stein. Introduction to Algorithms, Third Edition. The MIT
27 * Press, September 2009.
38 #include <urcu/rcurbtree.h>
39 #include <urcu-pointer.h>
40 #include <urcu-call-rcu.h>
45 #define dbg_printf(args...) printf(args)
47 #define dbg_printf(args...)
51 * Undefine this to enable the non-RCU rotate and transplant functions
54 #define RBTREE_RCU_SUPPORT_ROTATE_LEFT
55 #define RBTREE_RCU_SUPPORT_ROTATE_RIGHT
56 #define RBTREE_RCU_SUPPORT_TRANSPLANT
59 void set_decay(struct rcu_rbtree_node
*x
, struct rcu_rbtree_node
*xc
)
65 struct rcu_rbtree_node
*get_decay(struct rcu_rbtree_node
*x
)
75 struct rcu_rbtree_node
*is_decay(struct rcu_rbtree_node
*x
)
81 struct rcu_rbtree_node
*dup_decay_node(struct rcu_rbtree
*rbtree
,
82 struct rcu_rbtree_node
*x
)
84 struct rcu_rbtree_node
*xc
;
86 if (rcu_rbtree_is_nil(x
))
89 xc
= rbtree
->rballoc();
90 memcpy(xc
, x
, sizeof(struct rcu_rbtree_node
));
91 xc
->decay_next
= NULL
;
93 call_rcu(&x
->head
, rbtree
->rbfree
);
99 * Deal with memory allocation errors.
100 * Can be ensured by reserving a pool of memory entries before doing the
101 * insertion, which will have to be function of number of
102 * transplantations/rotations required for the operation.
106 void show_tree(struct rcu_rbtree
*rbtree
)
108 struct rcu_rbtree_node
*node
;
110 node
= rcu_rbtree_min(rbtree
, rbtree
->root
);
111 while (!rcu_rbtree_is_nil(node
)) {
112 assert(!is_decay(node
));
113 printf("{ 0x%lX p:%lX r:%lX l:%lX %s %s %s} ",
114 (unsigned long)node
->key
,
115 (unsigned long) node
->p
->key
,
116 (unsigned long) node
->right
->key
,
117 (unsigned long) node
->left
->key
,
118 node
->color
? "red" : "black",
119 node
->pos
? "right" : "left",
120 node
->nil
? "nil" : "");
121 node
= rcu_rbtree_next(rbtree
, node
);
127 struct rcu_rbtree_node
*make_nil(struct rcu_rbtree
*rbtree
)
129 return &rbtree
->nil_node
;
133 * Iterative rbtree search.
135 struct rcu_rbtree_node
* rcu_rbtree_search(struct rcu_rbtree
*rbtree
,
136 struct rcu_rbtree_node
*x
,
139 x
= rcu_dereference(x
);
141 while (!rcu_rbtree_is_nil(x
) && k
!= x
->key
) {
142 if (rbtree
->comp(k
, x
->key
) < 0)
143 x
= rcu_dereference(x
->left
);
145 x
= rcu_dereference(x
->right
);
150 struct rcu_rbtree_node
*rcu_rbtree_min(struct rcu_rbtree
*rbtree
,
151 struct rcu_rbtree_node
*x
)
153 struct rcu_rbtree_node
*xl
;
155 x
= rcu_dereference(x
);
157 if (rcu_rbtree_is_nil(x
))
160 while (!rcu_rbtree_is_nil(xl
= rcu_dereference(x
->left
)))
165 struct rcu_rbtree_node
*rcu_rbtree_max(struct rcu_rbtree
*rbtree
,
166 struct rcu_rbtree_node
*x
)
168 struct rcu_rbtree_node
*xr
;
170 x
= rcu_dereference(x
);
172 if (rcu_rbtree_is_nil(x
))
175 while (!rcu_rbtree_is_nil(xr
= rcu_dereference(x
->right
)))
182 * Updates should wait for a grace period between update of the
183 * redirect pointer and update of the parent child pointer. This is to make sure
184 * that no reference to the old entry exist.
188 * next and prev need to have mutex held to ensure that parent pointer is
191 struct rcu_rbtree_node
*rcu_rbtree_next(struct rcu_rbtree
*rbtree
,
192 struct rcu_rbtree_node
*x
)
194 struct rcu_rbtree_node
*xr
, *y
;
196 x
= rcu_dereference(x
);
198 if (!rcu_rbtree_is_nil(xr
= rcu_dereference(x
->right
)))
199 return rcu_rbtree_min(rbtree
, xr
);
200 y
= rcu_dereference(x
->p
);
201 while (!rcu_rbtree_is_nil(y
) && x
->pos
== IS_RIGHT
) {
203 y
= rcu_dereference(y
->p
);
208 struct rcu_rbtree_node
*rcu_rbtree_prev(struct rcu_rbtree
*rbtree
,
209 struct rcu_rbtree_node
*x
)
211 struct rcu_rbtree_node
*xl
, *y
;
213 x
= rcu_dereference(x
);
215 if (!rcu_rbtree_is_nil(xl
= rcu_dereference(x
->left
)))
216 return rcu_rbtree_min(rbtree
, xl
);
217 y
= rcu_dereference(x
->p
);
218 while (!rcu_rbtree_is_nil(y
) && x
->pos
== IS_LEFT
) {
220 y
= rcu_dereference(y
->p
);
226 * We have to ensure these assumptions are correct for prev/next
229 * with x being a right child, the assumption that:
231 * or if x is a left child, the assumption that:
234 * This explains why we have to allocate a vc copy of the node for left_rotate,
235 * right_rotate and transplant operations.
237 * We always ensure that the right/left child and correct parent is set in the
238 * node copies *before* we reparent the children and make the upper-level point
242 /* RCU: copy x and y, atomically point to new versions. GC old. */
243 /* Should be eventually followed by a cmm_smp_wmc() */
245 #ifdef RBTREE_RCU_SUPPORT_ROTATE_LEFT
248 void left_rotate(struct rcu_rbtree
*rbtree
,
249 struct rcu_rbtree_node
*x
)
251 struct rcu_rbtree_node
*y
, *y_left
, *x_p
;
257 /* Now operate on new copy, decay old versions */
258 x
= dup_decay_node(rbtree
, x
);
259 y
= dup_decay_node(rbtree
, y
);
260 y_left
= dup_decay_node(rbtree
, y_left
);
265 /* Internal node modifications */
272 if (!rcu_rbtree_is_nil(y_left
)) {
274 y_left
->pos
= IS_RIGHT
;
277 cmm_smp_wmb(); /* write into node before publish */
279 /* External references update (visible by readers) */
280 if (rcu_rbtree_is_nil(x_p
))
281 _CMM_STORE_SHARED(rbtree
->root
, y
);
282 else if (x_pos
== IS_LEFT
)
283 _CMM_STORE_SHARED(x_p
->left
, y
);
285 _CMM_STORE_SHARED(x_p
->right
, y
);
287 /* Point children to new copy (parent only used by updates/next/prev) */
288 x
->left
->p
= get_decay(x
->left
->p
);
289 y
->right
->p
= get_decay(y
->right
->p
);
290 if (!rcu_rbtree_is_nil(y_left
)) {
291 y_left
->right
->p
= get_decay(y_left
->right
->p
);
292 y_left
->left
->p
= get_decay(y_left
->left
->p
);
296 assert(y
== rbtree
->root
|| y
->p
->left
== y
|| y
->p
->right
== y
);
297 assert(x
== rbtree
->root
|| x
->p
->left
== x
|| x
->p
->right
== x
);
298 assert(rcu_rbtree_is_nil(x
->right
) || x
->right
->p
== x
);
299 assert(rcu_rbtree_is_nil(x
->left
) || x
->left
->p
== x
);
300 assert(rcu_rbtree_is_nil(y
->right
) || y
->right
->p
== y
);
301 assert(rcu_rbtree_is_nil(y
->left
) || y
->left
->p
== y
);
302 assert(!is_decay(rbtree
->root
));
303 assert(!is_decay(x
));
304 assert(!is_decay(y
));
305 assert(!is_decay(x
->right
));
306 assert(!is_decay(x
->left
));
307 assert(!is_decay(y
->right
));
308 assert(!is_decay(y
->left
));
313 /* non-rcu version */
315 void left_rotate(struct rcu_rbtree
*rbtree
,
316 struct rcu_rbtree_node
*x
)
318 struct rcu_rbtree_node
*y
;
322 if (!rcu_rbtree_is_nil(y
->left
)) {
324 y
->left
->pos
= IS_RIGHT
;
327 if (rcu_rbtree_is_nil(x
->p
))
329 else if (x
== x
->p
->left
) {
343 #ifdef RBTREE_RCU_SUPPORT_ROTATE_RIGHT
345 void right_rotate(struct rcu_rbtree
*rbtree
,
346 struct rcu_rbtree_node
*x
)
348 struct rcu_rbtree_node
*y
, *y_right
, *x_p
;
354 /* Now operate on new copy, decay old versions */
355 x
= dup_decay_node(rbtree
, x
);
356 y
= dup_decay_node(rbtree
, y
);
357 y_right
= dup_decay_node(rbtree
, y_right
);
362 /* Internal node modifications */
369 if (!rcu_rbtree_is_nil(y_right
)) {
371 y_right
->pos
= IS_LEFT
;
374 cmm_smp_wmb(); /* write into node before publish */
376 /* External references update (visible by readers) */
377 if (rcu_rbtree_is_nil(x_p
))
378 _CMM_STORE_SHARED(rbtree
->root
, y
);
379 else if (x_pos
== IS_RIGHT
)
380 _CMM_STORE_SHARED(x_p
->right
, y
);
382 _CMM_STORE_SHARED(x_p
->left
, y
);
384 /* Point children to new copy (parent only used by updates/next/prev) */
385 x
->right
->p
= get_decay(x
->right
->p
);
386 y
->left
->p
= get_decay(y
->left
->p
);
387 if (!rcu_rbtree_is_nil(y_right
)) {
388 y_right
->left
->p
= get_decay(y_right
->left
->p
);
389 y_right
->right
->p
= get_decay(y_right
->right
->p
);
393 assert(y
== rbtree
->root
|| y
->p
->right
== y
|| y
->p
->left
== y
);
394 assert(x
== rbtree
->root
|| x
->p
->right
== x
|| x
->p
->left
== x
);
395 assert(rcu_rbtree_is_nil(x
->left
) || x
->left
->p
== x
);
396 assert(rcu_rbtree_is_nil(x
->right
) || x
->right
->p
== x
);
397 assert(rcu_rbtree_is_nil(y
->left
) || y
->left
->p
== y
);
398 assert(rcu_rbtree_is_nil(y
->right
) || y
->right
->p
== y
);
399 assert(!is_decay(rbtree
->root
));
400 assert(!is_decay(x
));
401 assert(!is_decay(y
));
402 assert(!is_decay(x
->left
));
403 assert(!is_decay(x
->right
));
404 assert(!is_decay(y
->left
));
405 assert(!is_decay(y
->right
));
410 /* non-rcu version */
412 void right_rotate(struct rcu_rbtree
*rbtree
,
413 struct rcu_rbtree_node
*x
)
415 struct rcu_rbtree_node
*y
;
419 if (!rcu_rbtree_is_nil(y
->right
)) {
421 y
->right
->pos
= IS_LEFT
;
424 if (rcu_rbtree_is_nil(x
->p
))
426 else if (x
== x
->p
->right
) {
440 static void rcu_rbtree_insert_fixup(struct rcu_rbtree
*rbtree
,
441 struct rcu_rbtree_node
*z
)
443 struct rcu_rbtree_node
*y
;
445 dbg_printf("insert fixup %p\n", z
->key
);
446 assert(!is_decay(rbtree
->root
));
448 while (z
->p
->color
== COLOR_RED
) {
449 if (z
->p
== z
->p
->p
->left
) {
451 if (y
->color
== COLOR_RED
) {
452 z
->p
->color
= COLOR_BLACK
;
453 y
->color
= COLOR_BLACK
;
454 z
->p
->p
->color
= COLOR_RED
;
457 if (z
== z
->p
->right
) {
459 left_rotate(rbtree
, z
);
461 assert(!is_decay(rbtree
->root
));
463 z
->p
->color
= COLOR_BLACK
;
464 z
->p
->p
->color
= COLOR_RED
;
465 assert(!is_decay(z
));
466 assert(!is_decay(z
->p
));
467 assert(!is_decay(z
->p
->p
));
468 right_rotate(rbtree
, z
->p
->p
);
469 assert(!is_decay(z
));
470 assert(!is_decay(rbtree
->root
));
474 if (y
->color
== COLOR_RED
) {
475 z
->p
->color
= COLOR_BLACK
;
476 y
->color
= COLOR_BLACK
;
477 z
->p
->p
->color
= COLOR_RED
;
480 if (z
== z
->p
->left
) {
482 right_rotate(rbtree
, z
);
484 assert(!is_decay(rbtree
->root
));
486 z
->p
->color
= COLOR_BLACK
;
487 z
->p
->p
->color
= COLOR_RED
;
488 left_rotate(rbtree
, z
->p
->p
);
489 assert(!is_decay(z
));
490 assert(!is_decay(rbtree
->root
));
494 rbtree
->root
->color
= COLOR_BLACK
;
498 * rcu_rbtree_insert - Insert a node in the RCU rbtree
500 * Returns 0 on success, or < 0 on error.
502 int rcu_rbtree_insert(struct rcu_rbtree
*rbtree
,
503 struct rcu_rbtree_node
*z
)
505 struct rcu_rbtree_node
*x
, *y
;
507 dbg_printf("insert %p\n", z
->key
);
508 assert(!is_decay(rbtree
->root
));
510 y
= make_nil(rbtree
);
512 rbtree
->root
= make_nil(rbtree
);
514 while (!rcu_rbtree_is_nil(x
)) {
516 if (rbtree
->comp(z
->key
, x
->key
) < 0)
524 z
->left
= make_nil(rbtree
);
525 z
->right
= make_nil(rbtree
);
526 z
->color
= COLOR_RED
;
528 z
->decay_next
= NULL
;
530 if (rcu_rbtree_is_nil(y
))
531 z
->pos
= IS_RIGHT
; /* arbitrary for root node */
532 else if (rbtree
->comp(z
->key
, y
->key
) < 0)
538 * Order stores to z (children/parents) before stores that will make it
539 * visible to the rest of the tree.
543 if (rcu_rbtree_is_nil(y
))
544 _CMM_STORE_SHARED(rbtree
->root
, z
);
545 else if (rbtree
->comp(z
->key
, y
->key
) < 0)
546 _CMM_STORE_SHARED(y
->left
, z
);
548 _CMM_STORE_SHARED(y
->right
, z
);
549 rcu_rbtree_insert_fixup(rbtree
, z
);
551 * Make sure to commit all _CMM_STORE_SHARED() for non-coherent caches.
560 * Transplant v into u position.
563 #ifdef RBTREE_RCU_SUPPORT_TRANSPLANT
566 void rcu_rbtree_transplant(struct rcu_rbtree
*rbtree
,
567 struct rcu_rbtree_node
*u
,
568 struct rcu_rbtree_node
*v
)
570 dbg_printf("transplant %p\n", v
->key
);
572 if (!rcu_rbtree_is_nil(v
))
573 v
= dup_decay_node(rbtree
, v
);
575 if (rcu_rbtree_is_nil(u
->p
)) {
577 cmm_smp_wmb(); /* write into node before publish */
578 _CMM_STORE_SHARED(rbtree
->root
, v
);
579 } else if (u
== u
->p
->left
) {
582 cmm_smp_wmb(); /* write into node before publish */
583 _CMM_STORE_SHARED(u
->p
->left
, v
);
587 cmm_smp_wmb(); /* write into node before publish */
588 _CMM_STORE_SHARED(u
->p
->right
, v
);
590 /* Point children to new copy (parent only used by updates/next/prev) */
591 if (!rcu_rbtree_is_nil(v
)) {
592 v
->right
->p
= get_decay(v
->right
->p
);
593 v
->left
->p
= get_decay(v
->left
->p
);
595 assert(!is_decay(rbtree
->root
));
600 /* Non-RCU version */
602 void rcu_rbtree_transplant(struct rcu_rbtree
*rbtree
,
603 struct rcu_rbtree_node
*u
,
604 struct rcu_rbtree_node
*v
)
606 dbg_printf("transplant %p\n", v
->key
);
608 if (rcu_rbtree_is_nil(u
->p
))
610 else if (u
== u
->p
->left
) {
622 static void rcu_rbtree_remove_fixup(struct rcu_rbtree
*rbtree
,
623 struct rcu_rbtree_node
*x
)
625 dbg_printf("remove fixup %p\n", x
->key
);
627 while (x
!= rbtree
->root
&& x
->color
== COLOR_BLACK
) {
628 assert(!is_decay(x
->p
));
629 assert(!is_decay(x
->p
->left
));
630 if (x
== x
->p
->left
) {
631 struct rcu_rbtree_node
*w
;
635 if (w
->color
== COLOR_RED
) {
636 w
->color
= COLOR_BLACK
;
637 x
->p
->color
= COLOR_RED
;
638 left_rotate(rbtree
, x
->p
);
640 assert(!is_decay(rbtree
->root
));
643 if (w
->left
->color
== COLOR_BLACK
644 && w
->right
->color
== COLOR_BLACK
) {
645 w
->color
= COLOR_RED
;
647 assert(!is_decay(rbtree
->root
));
648 assert(!is_decay(x
));
650 if (w
->right
->color
== COLOR_BLACK
) {
651 w
->left
->color
= COLOR_BLACK
;
652 w
->color
= COLOR_RED
;
653 right_rotate(rbtree
, w
);
654 assert(!is_decay(rbtree
->root
));
658 w
->color
= x
->p
->color
;
659 x
->p
->color
= COLOR_BLACK
;
660 w
->right
->color
= COLOR_BLACK
;
661 left_rotate(rbtree
, x
->p
);
662 assert(!is_decay(rbtree
->root
));
666 struct rcu_rbtree_node
*w
;
670 if (w
->color
== COLOR_RED
) {
671 w
->color
= COLOR_BLACK
;
672 x
->p
->color
= COLOR_RED
;
673 right_rotate(rbtree
, x
->p
);
674 assert(!is_decay(rbtree
->root
));
678 if (w
->right
->color
== COLOR_BLACK
679 && w
->left
->color
== COLOR_BLACK
) {
680 w
->color
= COLOR_RED
;
682 assert(!is_decay(rbtree
->root
));
683 assert(!is_decay(x
));
685 if (w
->left
->color
== COLOR_BLACK
) {
686 w
->right
->color
= COLOR_BLACK
;
687 w
->color
= COLOR_RED
;
688 left_rotate(rbtree
, w
);
689 assert(!is_decay(rbtree
->root
));
693 w
->color
= x
->p
->color
;
694 x
->p
->color
= COLOR_BLACK
;
695 w
->left
->color
= COLOR_BLACK
;
696 right_rotate(rbtree
, x
->p
);
697 assert(!is_decay(rbtree
->root
));
702 x
->color
= COLOR_BLACK
;
706 * Delete z. All non-copied children left/right positions are unchanged.
709 void rcu_rbtree_remove_nonil(struct rcu_rbtree
*rbtree
,
710 struct rcu_rbtree_node
*z
,
711 struct rcu_rbtree_node
*y
)
713 struct rcu_rbtree_node
*x
;
715 dbg_printf("remove nonil %p\n", z
->key
);
718 assert(!is_decay(z
));
719 assert(!is_decay(y
));
720 assert(!is_decay(y
->right
));
721 assert(!is_decay(y
->p
));
723 assert(!is_decay(x
));
727 rcu_rbtree_transplant(rbtree
, y
, y
->right
);
728 assert(!is_decay(y
));
729 assert(!is_decay(z
));
730 assert(!is_decay(z
->right
));
734 rcu_rbtree_transplant(rbtree
, z
, y
);
736 assert(!is_decay(z
));
737 assert(!is_decay(z
->left
));
743 int rcu_rbtree_remove(struct rcu_rbtree
*rbtree
,
744 struct rcu_rbtree_node
*z
)
746 struct rcu_rbtree_node
*x
, *y
;
747 unsigned int y_original_color
;
749 assert(!is_decay(rbtree
->root
));
750 dbg_printf("remove %p\n", z
->key
);
753 assert(!is_decay(z
));
755 y_original_color
= y
->color
;
757 if (rcu_rbtree_is_nil(z
->left
)) {
758 rcu_rbtree_transplant(rbtree
, z
, z
->right
);
759 assert(!is_decay(z
));
760 x
= get_decay(z
->right
);
762 } else if (rcu_rbtree_is_nil(z
->right
)) {
763 rcu_rbtree_transplant(rbtree
, z
, z
->left
);
764 assert(!is_decay(z
));
765 x
= get_decay(z
->left
);
768 y
= rcu_rbtree_min(rbtree
, z
->right
);
769 assert(!is_decay(y
));
770 y_original_color
= y
->color
;
772 rcu_rbtree_remove_nonil(rbtree
, z
, y
);
776 if (y_original_color
== COLOR_BLACK
)
777 rcu_rbtree_remove_fixup(rbtree
, x
);
780 * Commit all _CMM_STORE_SHARED().
This page took 0.044214 seconds and 5 git commands to generate.