1 /******************************************************************************
2 * rb_lock_serialisedwriters.c
4 * Lock-based red-black trees, using multi-reader locks.
6 * Updates are serialised on a global mutual-exclusion spinlock.
8 * Updates never need to read-lock, as updates are serialised. Must write-lock
9 * for all node changes except colour changes and parent-link updates.
11 * Searches must read-lock down the tree, as they do not serialise.
13 * Copyright (c) 2002-2003, K A Fraser
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
19 * Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution. Neither the name of the Keir Fraser
25 * nor the names of its contributors may be used to endorse or
26 * promote products derived from this software without specific
27 * prior written permission.
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 #define __SET_IMPLEMENTATION__
48 #include "portable_defns.h"
52 #define IS_BLACK(_v) ((int_addr_t)(_v)&1)
53 #define IS_RED(_v) (!IS_BLACK(_v))
54 #define MK_BLACK(_v) ((setval_t)((int_addr_t)(_v)|1))
55 #define MK_RED(_v) ((setval_t)((int_addr_t)(_v)&~1))
56 #define GET_VALUE(_v) (MK_RED(_v))
57 #define GET_COLOUR(_v) (IS_BLACK(_v))
58 #define SET_COLOUR(_v,_c) ((setval_t)((unsigned long)(_v)|(unsigned long)(_c)))
60 typedef struct node_st node_t
;
61 typedef struct set_st set_t
;
75 mcs_lock_t writer_lock
;
81 static void left_rotate(node_t
*x
)
83 mrsw_qnode_t p_qn
, x_qn
, y_qn
;
84 node_t
*y
= x
->r
, *p
= x
->p
;
86 wr_lock(&p
->lock
, &p_qn
);
87 wr_lock(&x
->lock
, &x_qn
);
88 wr_lock(&y
->lock
, &y_qn
);
90 /* No need to write-lock to update parent link. */
91 if ( (x
->r
= y
->l
) != &null
) x
->r
->p
= x
;
96 if ( x
== p
->l
) p
->l
= y
; else p
->r
= y
;
98 wr_unlock(&y
->lock
, &y_qn
);
99 wr_unlock(&x
->lock
, &x_qn
);
100 wr_unlock(&p
->lock
, &p_qn
);
104 static void right_rotate(node_t
*x
)
106 mrsw_qnode_t p_qn
, x_qn
, y_qn
;
107 node_t
*y
= x
->l
, *p
= x
->p
;
109 wr_lock(&p
->lock
, &p_qn
);
110 wr_lock(&x
->lock
, &x_qn
);
111 wr_lock(&y
->lock
, &y_qn
);
113 /* No need to write-lock to update parent link. */
114 if ( (x
->l
= y
->r
) != &null
) x
->l
->p
= x
;
119 if ( x
== p
->l
) p
->l
= y
; else p
->r
= y
;
121 wr_unlock(&y
->lock
, &y_qn
);
122 wr_unlock(&x
->lock
, &x_qn
);
123 wr_unlock(&p
->lock
, &p_qn
);
127 /* No locks held on entry/exit. Colour changes safe. Rotations lock for us. */
128 static void delete_fixup(ptst_t
*ptst
, set_t
*s
, node_t
*x
)
132 while ( (x
->p
!= &s
->root
) && IS_BLACK(x
->v
) )
141 w
->v
= MK_BLACK(w
->v
);
143 /* Node W will be new parent of P. */
145 /* Get new sibling W. */
149 if ( IS_BLACK(w
->l
->v
) && IS_BLACK(w
->r
->v
) )
156 if ( IS_BLACK(w
->r
->v
) )
158 /* w->l is red => it cannot be null node. */
159 w
->l
->v
= MK_BLACK(w
->l
->v
);
162 /* Old w is new w->r. Old w->l is new w.*/
166 w
->v
= SET_COLOUR(GET_VALUE(w
->v
), GET_COLOUR(p
->v
));
167 p
->v
= MK_BLACK(p
->v
);
168 w
->r
->v
= MK_BLACK(w
->r
->v
);
173 else /* SYMMETRIC CASE */
178 w
->v
= MK_BLACK(w
->v
);
180 /* Node W will be new parent of P. */
182 /* Get new sibling W. */
186 if ( IS_BLACK(w
->l
->v
) && IS_BLACK(w
->r
->v
) )
193 if ( IS_BLACK(w
->l
->v
) )
195 /* w->r is red => it cannot be the null node. */
196 w
->r
->v
= MK_BLACK(w
->r
->v
);
199 /* Old w is new w->l. Old w->r is new w.*/
203 w
->v
= SET_COLOUR(GET_VALUE(w
->v
), GET_COLOUR(p
->v
));
204 p
->v
= MK_BLACK(p
->v
);
205 w
->l
->v
= MK_BLACK(w
->l
->v
);
212 x
->v
= MK_BLACK(x
->v
);
216 set_t
*set_alloc(void)
222 ptst
= critical_enter();
224 set
= (set_t
*)malloc(sizeof(*set
));
227 root
->k
= SENTINEL_KEYMIN
;
228 root
->v
= MK_RED(NULL
);
232 mrsw_init(&root
->lock
);
234 mcs_init(&set
->writer_lock
);
242 setval_t
set_update(set_t
*s
, setkey_t k
, setval_t v
, int overwrite
)
245 node_t
*x
, *p
, *g
, *y
, *new;
250 k
= CALLER_TO_INTERNAL_KEY(k
);
252 ptst
= critical_enter();
254 mcs_lock(&s
->writer_lock
, &writer_qn
);
257 while ( (y
= (k
< x
->k
) ? x
->l
: x
->r
) != &null
)
260 if ( k
== x
->k
) break;
266 /* Lock X to change mapping. */
267 wr_lock(&x
->lock
, &x_qn
);
268 if ( overwrite
) x
->v
= SET_COLOUR(v
, GET_COLOUR(ov
));
269 wr_unlock(&x
->lock
, &x_qn
);
276 new = (node_t
*)gc_alloc(ptst
, gc_id
);
282 mrsw_init(&new->lock
);
284 /* Lock X to change a child. */
285 wr_lock(&x
->lock
, &x_qn
);
286 if ( k
< x
->k
) x
->l
= new; else x
->r
= new;
287 wr_unlock(&x
->lock
, &x_qn
);
291 /* No locks held here. Colour changes safe. Rotations lock for us. */
294 if ( (p
= x
->p
) == &s
->root
)
296 x
->v
= MK_BLACK(x
->v
);
300 if ( IS_BLACK(p
->v
) ) break;
308 p
->v
= MK_BLACK(p
->v
);
309 y
->v
= MK_BLACK(y
->v
);
319 /* X and P switched round. */
322 p
->v
= MK_BLACK(p
->v
);
325 /* G no longer on the path. */
328 else /* SYMMETRIC CASE */
333 p
->v
= MK_BLACK(p
->v
);
334 y
->v
= MK_BLACK(y
->v
);
344 /* X and P switched round. */
347 p
->v
= MK_BLACK(p
->v
);
350 /* G no longer on the path. */
356 mcs_unlock(&s
->writer_lock
, &writer_qn
);
364 setval_t
set_remove(set_t
*s
, setkey_t k
)
368 mrsw_qnode_t qn
[2], *y_pqn
=qn
+0, *yp_pqn
=qn
+1, *t_pqn
;
369 mrsw_qnode_t z_qn
, zp_qn
;
373 k
= CALLER_TO_INTERNAL_KEY(k
);
375 ptst
= critical_enter();
377 mcs_lock(&s
->writer_lock
, &writer_qn
);
380 while ( (z
= (k
< z
->k
) ? z
->l
: z
->r
) != &null
)
382 if ( k
== z
->k
) break;
387 ov
= GET_VALUE(z
->v
);
389 if ( (z
->l
!= &null
) && (z
->r
!= &null
) )
391 /* Lock Z. It will get new key copied in. */
392 wr_lock(&z
->lock
, &z_qn
);
395 * Write-lock from Z to Y. We end up with (YP,Y) locked.
396 * Write-coupling is needed so we don't overtake searches for Y.
398 wr_lock(&y
->lock
, y_pqn
);
399 while ( y
->l
!= &null
)
401 if ( y
->p
!= z
) wr_unlock(&y
->p
->lock
, yp_pqn
);
406 wr_lock(&y
->lock
, y_pqn
);
412 /* Lock ZP. It will get new child. */
413 wr_lock(&z
->p
->lock
, &zp_qn
);
414 /* Lock Z. It will be deleted. */
415 wr_lock(&z
->lock
, &z_qn
);
418 /* No need to lock X. Only parent link is modified. */
419 x
= (y
->l
!= &null
) ? y
->l
: y
->r
;
422 if ( y
== y
->p
->l
) y
->p
->l
= x
; else y
->p
->r
= x
;
427 z
->v
= SET_COLOUR(GET_VALUE(y
->v
), GET_COLOUR(z
->v
));
428 if ( y
->p
!= z
) wr_unlock(&y
->p
->lock
, yp_pqn
);
429 wr_unlock(&y
->lock
, y_pqn
);
433 wr_unlock(&z
->p
->lock
, &zp_qn
);
436 wr_unlock(&z
->lock
, &z_qn
);
438 gc_free(ptst
, y
, gc_id
);
440 if ( IS_BLACK(y
->v
) ) delete_fixup(ptst
, s
, x
);
443 mcs_unlock(&s
->writer_lock
, &writer_qn
);
451 setval_t
set_lookup(set_t
*s
, setkey_t k
)
455 mrsw_qnode_t qn
[2], *m_pqn
=&qn
[0], *n_pqn
=&qn
[1], *t_pqn
;
458 k
= CALLER_TO_INTERNAL_KEY(k
);
460 ptst
= critical_enter();
463 rd_lock(&n
->lock
, n_pqn
);
465 while ( (m
= (k
< n
->k
) ? n
->l
: n
->r
) != &null
)
467 rd_lock(&m
->lock
, m_pqn
);
468 rd_unlock(&n
->lock
, n_pqn
);
480 rd_unlock(&n
->lock
, n_pqn
);
488 void _init_set_subsystem(void)
490 gc_id
= gc_add_allocator(sizeof(node_t
));
493 null
.v
= MK_BLACK(NULL
);
497 mrsw_init(&null
.lock
);