1 /******************************************************************************
4 * Lock-free binary serach trees (BSTs), based on per-node spinlocks.
5 * Uses threaded tree presentation as described in my PhD dissertation:
6 * "Practical Lock-Freedom", University of Cambridge, 2003.
8 * Copyright (c) 2002-2003, K A Fraser
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are
14 * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution. Neither the name of the Keir Fraser
20 * nor the names of its contributors may be used to endorse or
21 * promote products derived from this software without specific
22 * prior written permission.
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #define __SET_IMPLEMENTATION__
47 #include <sys/types.h>
52 #include "portable_defns.h"
57 #define THREAD(_p) ((node_t *)((int_addr_t)(_p)|(MARK_THREAD)))
58 #define UNTHREAD(_p) ((node_t *)((int_addr_t)(_p)&~MARK_THREAD))
59 #define IS_THREAD(_p) ((int)((int_addr_t)(_p)&MARK_THREAD))
61 #define IS_GARBAGE(_n) ((_n)->v == NULL)
63 typedef struct node_st node_t
;
64 typedef struct set_st set_t
;
82 /* We use these flags to determine whch nodes are currently locked. */
85 #define PAL_LOCKED 0x04
86 #define PAR_LOCKED 0x08
87 #define AL_LOCKED 0x10
88 #define AR_LOCKED 0x20
90 #define LOCK(_n, _qn, _flag) \
92 mcs_lock(&(_n)->lock, &(_qn)); \
93 if ( IS_GARBAGE(_n) ) { \
94 mcs_unlock(&(_n)->lock, &(_qn)); \
97 lock_flags |= (_flag); \
100 #define UNLOCK(_n, _qn, _flag) \
102 if ( (lock_flags & (_flag)) ) \
103 mcs_unlock(&(_n)->lock, &(_qn)); \
108 * Search for node with key == k. Return NULL if none such, else ptr to node.
109 * @ppn is filled in with parent node, or closest leaf if no match.
110 * p and n will both be unmarked and adjacent on return.
112 static node_t
*search(set_t
*s
, setkey_t k
, node_t
**ppn
)
120 while ( !IS_THREAD(n
) )
124 assert(UNTHREAD(c
)->k
< n
->k
);
125 } else if ( k
> n
->k
) {
127 assert(UNTHREAD(c
)->k
> n
->k
);
128 } else /* k == n->k */
134 /* Follow final thread, just in case. */
136 if ( k
== c
->k
) goto followed_thread
;
143 if ( ppn
) { RMB(); goto retry
; }
148 set_t
*set_alloc(void)
152 s
= malloc(sizeof(*s
));
153 mcs_init(&s
->root
.lock
);
154 s
->root
.k
= SENTINEL_KEYMIN
;
155 s
->root
.v
= (setval_t
)(~0UL);
156 s
->root
.l
= THREAD(&s
->root
);
157 s
->root
.r
= THREAD(&s
->sentinel
);
159 mcs_init(&s
->sentinel
.lock
);
160 s
->sentinel
.k
= SENTINEL_KEYMAX
;
166 setval_t
set_update(set_t
*s
, setkey_t k
, setval_t v
, int overwrite
)
169 node_t
*p
, *n
, *new = NULL
;
172 int lock_flags
, r
= 0;
174 k
= CALLER_TO_INTERNAL_KEY(k
);
176 ptst
= critical_enter();
182 n
= search(s
, k
, &p
);
186 LOCK(n
, qn
, N_LOCKED
);
188 if ( overwrite
) n
->v
= v
;
194 new = gc_alloc(ptst
, gc_id
);
195 mcs_init(&new->lock
);
200 LOCK(p
, qp
, P_LOCKED
);
204 if ( (p
->r
!= n
) || (UNTHREAD(n
)->k
< k
) ) goto retry
;
212 if ( (p
->l
!= n
) || (UNTHREAD(n
)->k
> k
) ) goto retry
;
219 new = NULL
; /* node is now in tree */
225 UNLOCK(p
, qp
, P_LOCKED
);
226 UNLOCK(n
, qn
, N_LOCKED
);
230 if ( new ) gc_free(ptst
, new, gc_id
);
236 #define FIND_HELPER(_d1, _d2, _n, _ap, _a) \
242 while ( !IS_THREAD(ac) ) \
252 * Order of first two cases does matter! If @n is the left-link of @p, then
253 * we use DELETE_HELPER(l, r). What matters is what we do when @n is a leaf.
254 * In this case we end up choosing n->l to propagate to p->l -- this
255 * happens to be the correct choice :-)
257 * NB. Note symmetric deletion cases dependent on parameter @dir. We
258 * could simplify the algorithm by always following one direction. In fact,
259 * that is slightly worse, or much worse, depending on the chosen case
260 * (hint: works best with dir hardwired to zero :-)....
263 #define DELETE_HELPER(_d1, _d2) \
264 FIND_HELPER(_d1, _d2, n, pal, al); \
265 FIND_HELPER(_d2, _d1, n, par, ar); \
266 if ( IS_THREAD(n ## _d2) ) \
268 if ( IS_THREAD(n ## _d1) ) \
274 LOCK(al, qal, AL_LOCKED); \
275 if ( al->_d2 != THREAD(n) ) goto retry; \
277 al->_d2 = n ## _d2; \
280 else if ( IS_THREAD(n ## _d1) ) \
282 LOCK(ar, qar, AR_LOCKED); \
283 if ( ar->_d1 != THREAD(n) ) goto retry; \
285 ar->_d1 = n ## _d1; \
291 LOCK(par, qpar, PAR_LOCKED); \
292 if ( par->_d1 != ar ) goto retry; \
294 LOCK(al, qal, AL_LOCKED); \
295 LOCK(ar, qar, AR_LOCKED); \
296 if ( (al->_d2 != THREAD(n)) || (ar->_d1 != THREAD(n)) ) goto retry; \
297 al->_d2 = THREAD(ar); \
298 ar->_d1 = n ## _d1; \
302 ar->_d2 = n ## _d2; \
303 par->_d1 = IS_THREAD(ac) ? THREAD(ar) : ac; \
305 WMB(); /* New links in AR must appear before it is raised. */ \
312 LOCK(pal, qpal, PAL_LOCKED); \
313 if ( pal->_d2 != al ) goto retry; \
315 LOCK(al, qal, AL_LOCKED); \
316 LOCK(ar, qar, AR_LOCKED); \
317 if ( (al->_d2 != THREAD(n)) || (ar->_d1 != THREAD(n)) ) goto retry; \
318 al->_d2 = n ## _d2; \
319 ar->_d1 = THREAD(al); \
323 al->_d1 = n ## _d1; \
324 pal->_d2 = IS_THREAD(ac) ? THREAD(al) : ac; \
326 WMB(); /* New links in AL must appear before it is raised. */ \
331 /* @k: key of node to be deleted */
332 setval_t
set_remove(set_t
*s
, setkey_t k
)
334 node_t
*p
, *n
, *nl
, *nr
, *al
, *ar
, *pal
, *par
, *ac
, **p_pc
;
335 qnode_t qp
, qn
, qal
, qar
, qpal
, qpar
;
336 int r
= 0, lock_flags
;
340 k
= CALLER_TO_INTERNAL_KEY(k
);
342 ptst
= critical_enter();
348 n
= search(s
, k
, &p
);
349 if ( IS_THREAD(n
) ) goto out
;
351 LOCK(p
, qp
, P_LOCKED
);
352 p_pc
= (p
->k
> n
->k
) ? &p
->l
: &p
->r
;
353 if ( *p_pc
!= n
) goto retry
;
355 LOCK(n
, qn
, N_LOCKED
);
362 /* @n is leftwards link from @p. */
367 /* @n is rightwards link from @p. */
376 UNLOCK(p
, qp
, P_LOCKED
);
377 UNLOCK(n
, qn
, N_LOCKED
);
378 UNLOCK(pal
, qpal
, PAL_LOCKED
);
379 UNLOCK(par
, qpar
, PAR_LOCKED
);
380 UNLOCK(al
, qal
, AL_LOCKED
);
381 UNLOCK(ar
, qar
, AR_LOCKED
);
385 gc_free(ptst
, n
, gc_id
);
393 setval_t
set_lookup(set_t
*s
, setkey_t k
)
399 k
= CALLER_TO_INTERNAL_KEY(k
);
401 ptst
= critical_enter();
403 n
= search(s
, k
, NULL
);
404 v
= (!IS_THREAD(n
)) ? n
->v
: NULL
;
411 void _init_set_subsystem(void)
413 gc_id
= gc_add_allocator(sizeof(node_t
));