Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / mcas / bst_lock_fraser.c
blob4c03355e52183cbf66089490b310da0c0e4468dc
1 /******************************************************************************
2 * bst_lock_fraser.c
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
12 met:
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__
40 #include <ucontext.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <limits.h>
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <assert.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <stdarg.h>
52 #include "portable_defns.h"
53 #include "gc.h"
54 #include "set.h"
56 #define MARK_THREAD 1
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;
66 struct node_st
68 setkey_t k;
69 setval_t v;
70 node_t *l, *r;
71 mcs_lock_t lock;
74 struct set_st
76 node_t root;
77 node_t sentinel;
80 static int gc_id;
82 /* We use these flags to determine whch nodes are currently locked. */
83 #define P_LOCKED 0x01
84 #define N_LOCKED 0x02
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) \
91 do { \
92 mcs_lock(&(_n)->lock, &(_qn)); \
93 if ( IS_GARBAGE(_n) ) { \
94 mcs_unlock(&(_n)->lock, &(_qn)); \
95 goto retry; \
96 } \
97 lock_flags |= (_flag); \
98 } while ( 0 )
100 #define UNLOCK(_n, _qn, _flag) \
101 do { \
102 if ( (lock_flags & (_flag)) ) \
103 mcs_unlock(&(_n)->lock, &(_qn)); \
104 } while ( 0 )
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)
114 node_t *p, *n, *c;
116 retry:
117 p = &s->root;
118 n = p->r;
120 while ( !IS_THREAD(n) )
122 if ( k < n->k ) {
123 c = n->l;
124 assert(UNTHREAD(c)->k < n->k);
125 } else if ( k > n->k ) {
126 c = n->r;
127 assert(UNTHREAD(c)->k > n->k);
128 } else /* k == n->k */
129 goto found;
131 p = n; n = c;
134 /* Follow final thread, just in case. */
135 c = UNTHREAD(n);
136 if ( k == c->k ) goto followed_thread;
138 found:
139 if ( ppn ) *ppn = p;
140 return n;
142 followed_thread:
143 if ( ppn ) { RMB(); goto retry; }
144 return c;
148 set_t *set_alloc(void)
150 set_t *s;
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;
162 return s;
166 setval_t set_update(set_t *s, setkey_t k, setval_t v, int overwrite)
168 setval_t ov;
169 node_t *p, *n, *new = NULL;
170 qnode_t qp, qn;
171 ptst_t *ptst;
172 int lock_flags, r = 0;
174 k = CALLER_TO_INTERNAL_KEY(k);
176 ptst = critical_enter();
178 do {
179 ov = NULL;
180 lock_flags = 0;
182 n = search(s, k, &p);
184 if ( !IS_THREAD(n) )
186 LOCK(n, qn, N_LOCKED);
187 ov = n->v;
188 if ( overwrite ) n->v = v;
190 else
192 if ( new == NULL )
194 new = gc_alloc(ptst, gc_id);
195 mcs_init(&new->lock);
196 new->k = k;
197 new->v = v;
200 LOCK(p, qp, P_LOCKED);
202 if ( p->k < k )
204 if ( (p->r != n) || (UNTHREAD(n)->k < k) ) goto retry;
205 new->l = THREAD(p);
206 new->r = n;
207 WMB();
208 p->r = new;
210 else
212 if ( (p->l != n) || (UNTHREAD(n)->k > k) ) goto retry;
213 new->l = n;
214 new->r = THREAD(p);
215 WMB();
216 p->l = new;
219 new = NULL; /* node is now in tree */
222 r = 1; /* success */
224 retry:
225 UNLOCK(p, qp, P_LOCKED);
226 UNLOCK(n, qn, N_LOCKED);
228 while ( !r );
230 if ( new ) gc_free(ptst, new, gc_id);
231 critical_exit(ptst);
232 return ov;
236 #define FIND_HELPER(_d1, _d2, _n, _ap, _a) \
238 node_t *ac; \
239 (_ap) = NULL; \
240 (_a) = (_n); \
241 ac = (_a)->_d1; \
242 while ( !IS_THREAD(ac) ) \
244 (_ap) = (_a); \
245 (_a) = ac; \
246 ac = (_a)->_d2; \
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 :-)....
262 #define dir 0
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) ) \
270 *p_pc = n ## _d1; \
272 else \
274 LOCK(al, qal, AL_LOCKED); \
275 if ( al->_d2 != THREAD(n) ) goto retry; \
276 *p_pc = n ## _d1; \
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; \
284 *p_pc = n ## _d2; \
285 ar->_d1 = n ## _d1; \
287 else if ( dir ) \
289 if ( par != n ) \
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; \
299 if ( par != n ) \
301 ac = ar->_d2; \
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. */ \
306 *p_pc = ar; \
308 else \
310 if ( pal != n ) \
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); \
320 if ( pal != n ) \
322 ac = al->_d1; \
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. */ \
327 *p_pc = al; \
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;
337 setval_t v;
338 ptst_t *ptst;
340 k = CALLER_TO_INTERNAL_KEY(k);
342 ptst = critical_enter();
344 do {
345 v = NULL;
346 lock_flags = 0;
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);
357 nl = n->l;
358 nr = n->r;
360 if ( p->k > n->k )
362 /* @n is leftwards link from @p. */
363 DELETE_HELPER(l, r);
365 else
367 /* @n is rightwards link from @p. */
368 DELETE_HELPER(r, l);
371 r = 1;
372 v = n->v;
373 n->v = NULL;
375 retry:
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);
383 while ( !r );
385 gc_free(ptst, n, gc_id);
387 out:
388 critical_exit(ptst);
389 return v;
393 setval_t set_lookup(set_t *s, setkey_t k)
395 node_t *n;
396 setval_t v;
397 ptst_t *ptst;
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;
406 critical_exit(ptst);
407 return v;
411 void _init_set_subsystem(void)
413 gc_id = gc_add_allocator(sizeof(node_t));