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.
37 #include <urcu/rcurbtree.h>
38 #include <urcu-pointer.h>
39 #include <urcu-defer.h>
44 #define dbg_printf(args...) printf(args)
46 #define dbg_printf(args...)
51 * Deal with memory allocation errors.
52 * Can be ensured by reserving a pool of memory entries before doing the
53 * insertion, which will have to be function of number of
54 * transplantations/rotations required for the operation.
58 void show_tree(struct rcu_rbtree
*rbtree
)
60 struct rcu_rbtree_node
*node
;
62 node
= rcu_rbtree_min(rbtree
, rbtree
->root
);
63 while (!rcu_rbtree_is_nil(node
)) {
64 printf("{ 0x%lX p:%lX r:%lX l:%lX %s %s %s} ",
65 (unsigned long)node
->key
,
69 node
->color
? "red" : "black",
70 node
->pos
? "right" : "left",
71 node
->nil
? "nil" : "");
72 node
= rcu_rbtree_next(rbtree
, node
);
78 struct rcu_rbtree_node
*make_nil(struct rcu_rbtree
*rbtree
)
80 return &rbtree
->nil_node
;
84 * Iterative rbtree search.
86 struct rcu_rbtree_node
* rcu_rbtree_search(struct rcu_rbtree
*rbtree
,
87 struct rcu_rbtree_node
*x
,
90 x
= rcu_dereference(x
);
92 while (!rcu_rbtree_is_nil(x
) && k
!= x
->key
) {
93 if (rbtree
->comp(k
, x
->key
) < 0)
94 x
= rcu_dereference(x
->left
);
96 x
= rcu_dereference(x
->right
);
101 struct rcu_rbtree_node
*rcu_rbtree_min(struct rcu_rbtree
*rbtree
,
102 struct rcu_rbtree_node
*x
)
104 struct rcu_rbtree_node
*xl
;
106 x
= rcu_dereference(x
);
108 if (rcu_rbtree_is_nil(x
))
111 while (!rcu_rbtree_is_nil(xl
= rcu_dereference(x
->left
)))
116 struct rcu_rbtree_node
*rcu_rbtree_max(struct rcu_rbtree
*rbtree
,
117 struct rcu_rbtree_node
*x
)
119 struct rcu_rbtree_node
*xr
;
121 x
= rcu_dereference(x
);
123 if (rcu_rbtree_is_nil(x
))
126 while (!rcu_rbtree_is_nil(xr
= rcu_dereference(x
->right
)))
133 * Updates should wait for a grace period between update of the
134 * redirect pointer and update of the parent child pointer. This is to make sure
135 * that no reference to the old entry exist.
139 * next and prev need to have mutex held to ensure that parent pointer is
142 struct rcu_rbtree_node
*rcu_rbtree_next(struct rcu_rbtree
*rbtree
,
143 struct rcu_rbtree_node
*x
)
145 struct rcu_rbtree_node
*xr
, *y
, *yr
;
147 x
= rcu_dereference(x
);
149 if (!rcu_rbtree_is_nil(xr
= rcu_dereference(x
->right
)))
150 return rcu_rbtree_min(rbtree
, xr
);
151 y
= rcu_dereference(x
->p
);
152 while (!rcu_rbtree_is_nil(y
) && x
->pos
== IS_RIGHT
) {
154 y
= rcu_dereference(y
->p
);
159 struct rcu_rbtree_node
*rcu_rbtree_prev(struct rcu_rbtree
*rbtree
,
160 struct rcu_rbtree_node
*x
)
162 struct rcu_rbtree_node
*xl
, *y
, *yl
;
164 x
= rcu_dereference(x
);
166 if (!rcu_rbtree_is_nil(xl
= rcu_dereference(x
->left
)))
167 return rcu_rbtree_min(rbtree
, xl
);
168 y
= rcu_dereference(x
->p
);
169 while (!rcu_rbtree_is_nil(y
) && x
->pos
== IS_LEFT
) {
171 y
= rcu_dereference(y
->p
);
177 * We have to ensure these assumptions are correct for prev/next
180 * with x being a right child, the assumption that:
182 * or if x is a left child, the assumption that:
185 * This explains why we have to allocate a vc copy of the node for left_rotate,
186 * right_rotate and transplant operations.
188 * We always ensure that the right/left child and correct parent is set in the
189 * node copies *before* we reparent the children and make the upper-level point
193 /* RCU: copy x and y, atomically point to new versions. GC old. */
194 /* Should be eventually followed by a cmm_smp_wmc() */
195 /* Returns the new x. Previous x->right references are changed to yc.
196 * Previous y->left->right is changed to bc. */
199 struct rcu_rbtree_node
*left_rotate(struct rcu_rbtree
*rbtree
,
200 struct rcu_rbtree_node
*x
)
202 struct rcu_rbtree_node
*y
;
203 struct rcu_rbtree_node
*t
;
208 if (!rcu_rbtree_is_nil(y
->left
)) {
210 y
->left
->pos
= IS_RIGHT
;
213 if (rcu_rbtree_is_nil(x
->p
))
215 else if (x
== x
->p
->left
) {
229 struct rcu_rbtree_node
*right_rotate(struct rcu_rbtree
*rbtree
,
230 struct rcu_rbtree_node
*x
)
232 struct rcu_rbtree_node
*y
;
233 struct rcu_rbtree_node
*t
;
238 if (!rcu_rbtree_is_nil(y
->right
)) {
240 y
->right
->pos
= IS_LEFT
;
243 if (rcu_rbtree_is_nil(x
->p
))
245 else if (x
== x
->p
->right
) {
258 static void rcu_rbtree_insert_fixup(struct rcu_rbtree
*rbtree
,
259 struct rcu_rbtree_node
*z
)
261 struct rcu_rbtree_node
*y
;
263 dbg_printf("insert fixup %p\n", z
->key
);
265 while (z
->p
->color
== COLOR_RED
) {
266 if (z
->p
== z
->p
->p
->left
) {
268 if (y
->color
== COLOR_RED
) {
269 z
->p
->color
= COLOR_BLACK
;
270 y
->color
= COLOR_BLACK
;
271 z
->p
->p
->color
= COLOR_RED
;
274 if (z
== z
->p
->right
) {
276 left_rotate(rbtree
, z
);
278 z
->p
->color
= COLOR_BLACK
;
279 z
->p
->p
->color
= COLOR_RED
;
280 right_rotate(rbtree
, z
->p
->p
);
284 if (y
->color
== COLOR_RED
) {
285 z
->p
->color
= COLOR_BLACK
;
286 y
->color
= COLOR_BLACK
;
287 z
->p
->p
->color
= COLOR_RED
;
290 if (z
== z
->p
->left
) {
292 right_rotate(rbtree
, z
);
294 z
->p
->color
= COLOR_BLACK
;
295 z
->p
->p
->color
= COLOR_RED
;
296 left_rotate(rbtree
, z
->p
->p
);
300 rbtree
->root
->color
= COLOR_BLACK
;
304 * rcu_rbtree_insert - Insert a node in the RCU rbtree
306 * Returns 0 on success, or < 0 on error.
308 int rcu_rbtree_insert(struct rcu_rbtree
*rbtree
,
309 struct rcu_rbtree_node
*z
)
311 struct rcu_rbtree_node
*x
, *y
;
313 dbg_printf("insert %p\n", z
->key
);
315 y
= make_nil(rbtree
);
317 rbtree
->root
= make_nil(rbtree
);
319 while (!rcu_rbtree_is_nil(x
)) {
321 if (rbtree
->comp(z
->key
, x
->key
) < 0)
329 z
->left
= make_nil(rbtree
);
330 z
->right
= make_nil(rbtree
);
331 z
->color
= COLOR_RED
;
334 if (rcu_rbtree_is_nil(y
))
335 z
->pos
= IS_RIGHT
; /* arbitrary for root node */
336 else if (rbtree
->comp(z
->key
, y
->key
) < 0)
342 * Order stores to z (children/parents) before stores that will make it
343 * visible to the rest of the tree.
347 if (rcu_rbtree_is_nil(y
))
348 _CMM_STORE_SHARED(rbtree
->root
, z
);
349 else if (rbtree
->comp(z
->key
, y
->key
) < 0)
350 _CMM_STORE_SHARED(y
->left
, z
);
352 _CMM_STORE_SHARED(y
->right
, z
);
353 rcu_rbtree_insert_fixup(rbtree
, z
);
355 * Make sure to commit all _CMM_STORE_SHARED() for non-coherent caches.
364 * Transplant v into u position.
365 * Returns new copy of v.
367 static struct rcu_rbtree_node
*
368 rcu_rbtree_transplant(struct rcu_rbtree
*rbtree
,
369 struct rcu_rbtree_node
*u
,
370 struct rcu_rbtree_node
*v
)
372 struct rcu_rbtree_node
*vc
;
374 dbg_printf("transplant %p\n", v
->key
);
376 if (rcu_rbtree_is_nil(u
->p
))
378 else if (u
== u
->p
->left
) {
389 static void rcu_rbtree_remove_fixup(struct rcu_rbtree
*rbtree
,
390 struct rcu_rbtree_node
*x
)
392 dbg_printf("remove fixup %p\n", x
->key
);
394 while (x
!= rbtree
->root
&& x
->color
== COLOR_BLACK
) {
395 if (x
== x
->p
->left
) {
396 struct rcu_rbtree_node
*w
, *t
;
400 if (w
->color
== COLOR_RED
) {
401 w
->color
== COLOR_BLACK
;
402 x
->p
->color
= COLOR_RED
;
403 t
= left_rotate(rbtree
, x
->p
);
404 assert(x
->p
->left
== t
->left
);
405 /* x is a left node, not copied by rotation */
408 if (w
->left
->color
== COLOR_BLACK
409 && w
->right
->color
== COLOR_BLACK
) {
410 w
->color
= COLOR_RED
;
413 if (w
->right
->color
== COLOR_BLACK
) {
414 w
->left
->color
= COLOR_BLACK
;
415 w
->color
= COLOR_RED
;
416 right_rotate(rbtree
, w
);
419 w
->color
= x
->p
->color
;
420 x
->p
->color
= COLOR_BLACK
;
421 w
->right
->color
= COLOR_BLACK
;
422 left_rotate(rbtree
, x
->p
);
426 struct rcu_rbtree_node
*w
, *t
;
430 if (w
->color
== COLOR_RED
) {
431 w
->color
== COLOR_BLACK
;
432 x
->p
->color
= COLOR_RED
;
433 t
= right_rotate(rbtree
, x
->p
);
434 assert(x
->p
->right
== t
->right
);
435 /* x is a right node, not copied by rotation */
438 if (w
->right
->color
== COLOR_BLACK
439 && w
->left
->color
== COLOR_BLACK
) {
440 w
->color
= COLOR_RED
;
443 if (w
->left
->color
== COLOR_BLACK
) {
444 w
->right
->color
= COLOR_BLACK
;
445 w
->color
= COLOR_RED
;
446 left_rotate(rbtree
, w
);
449 w
->color
= x
->p
->color
;
450 x
->p
->color
= COLOR_BLACK
;
451 w
->left
->color
= COLOR_BLACK
;
452 right_rotate(rbtree
, x
->p
);
457 x
->color
= COLOR_BLACK
;
461 * Returns the new copy of y->right.
462 * Delete z. All non-copied children left/right positions are unchanged. */
463 static struct rcu_rbtree_node
*
464 rcu_rbtree_remove_nonil(struct rcu_rbtree
*rbtree
,
465 struct rcu_rbtree_node
*z
,
466 struct rcu_rbtree_node
*y
)
468 struct rcu_rbtree_node
*x
, *xc
, *yc
;
470 dbg_printf("remove nonil %p\n", z
->key
);
477 rcu_rbtree_transplant(rbtree
, y
, y
->right
);
481 rcu_rbtree_transplant(rbtree
, z
, y
);
488 int rcu_rbtree_remove(struct rcu_rbtree
*rbtree
,
489 struct rcu_rbtree_node
*z
)
491 struct rcu_rbtree_node
*x
, *y
;
492 unsigned int y_original_color
;
494 dbg_printf("remove %p\n", z
->key
);
498 y_original_color
= y
->color
;
500 if (rcu_rbtree_is_nil(z
->left
)) {
501 x
= rcu_rbtree_transplant(rbtree
, z
, z
->right
);
503 } else if (rcu_rbtree_is_nil(z
->right
)) {
504 x
= rcu_rbtree_transplant(rbtree
, z
, z
->left
);
507 y
= rcu_rbtree_min(rbtree
, z
->right
);
508 y_original_color
= y
->color
;
509 x
= rcu_rbtree_remove_nonil(rbtree
, z
, y
);
512 if (y_original_color
== COLOR_BLACK
)
513 rcu_rbtree_remove_fixup(rbtree
, x
);
516 * Commit all _CMM_STORE_SHARED().
This page took 0.039196 seconds and 5 git commands to generate.