1 /******************************************************************************
4 * Lock-based binary search trees (BSTs), based on:
5 * Udi Manber and Richard E. Ladner.
6 * "Concurrency control in a dynamic search structure".
7 * ACM Transactions on Database Systems, Vol. 9, No. 3, September 1984.
9 * 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.
37 #define __SET_IMPLEMENTATION__
46 #include <sys/types.h>
51 #include "portable_defns.h"
55 #define GARBAGE_FLAG 1
56 #define REDUNDANT_FLAG 2
58 #define IS_GARBAGE(_n) ((int)(_n)->v & GARBAGE_FLAG)
59 #define MK_GARBAGE(_n) \
60 ((_n)->v = (setval_t)((unsigned long)(_n)->v | GARBAGE_FLAG))
62 #define IS_REDUNDANT(_n) ((int)(_n)->v & REDUNDANT_FLAG)
63 #define MK_REDUNDANT(_n) \
64 ((_n)->v = (setval_t)((unsigned long)(_n)->v | REDUNDANT_FLAG))
66 #define GET_VALUE(_n) ((setval_t)((unsigned long)(_n)->v & ~3UL))
68 #define FOLLOW(_n, _k) (((_n)->k < (_k)) ? (_n)->r : (_n)->l)
70 typedef struct node_st node_t
;
71 typedef struct set_st set_t
;
87 static int gc_id
, hook_id
;
89 #define LOCK(_n, _pqn) mcs_lock(&(_n)->lock, (_pqn))
90 #define UNLOCK(_n, _pqn) mcs_unlock(&(_n)->lock, (_pqn))
93 static node_t
*weak_search(node_t
*n
, setkey_t k
)
95 while ( (n
!= NULL
) && (n
->k
!= k
) ) n
= FOLLOW(n
, k
);
100 static node_t
*strong_search(node_t
*n
, setkey_t k
, qnode_t
*qn
)
103 node_t
*a
= FOLLOW(b
, k
);
106 while ( (a
!= NULL
) && (a
->k
!= k
) )
121 else if ( (a
= FOLLOW(b
, k
)) != NULL
)
138 else if ( IS_REDUNDANT(a
) )
150 static void redundancy_removal(ptst_t
*ptst
, void *x
)
156 if ( x
== NULL
) return;
163 r
= weak_search(e
->l
, k
);
164 assert((r
== NULL
) || !IS_REDUNDANT(r
) || (r
->r
== e
));
166 redundancy_removal(ptst
, r
);
170 if ( IS_GARBAGE(e
) ) return;
173 if ( IS_GARBAGE(d
) ) UNLOCK(d
, &d_qn
);
175 while ( IS_GARBAGE(d
) );
179 if ( IS_GARBAGE(e
) || !IS_REDUNDANT(e
) ) goto out_de
;
191 assert(e
->r
!= NULL
);
192 assert(e
->r
->k
== k
);
194 assert(!IS_GARBAGE(e
->r
));
199 if ( e
->l
!= NULL
) e
->l
->p
= d
;
203 gc_free(ptst
, e
, gc_id
);
211 /* NB. Node X is not locked on entry. */
212 static void predecessor_substitution(ptst_t
*ptst
, set_t
*s
, node_t
*x
)
214 node_t
*a
, *b
, *e
, *f
, **pac
;
215 qnode_t a_qn
, b_qn
, e_qn
, f_qn
;
222 if ( (b
== NULL
) || (b
->v
!= NULL
) ) return;
225 if ( IS_GARBAGE(a
) ) UNLOCK(a
, &a_qn
);
227 while ( IS_GARBAGE(a
) );
234 * 1. The node is already deleted (and is thus garbage); or
235 * 2. The node is redundant (redundancy removal will do it); or
236 * 3. The node has been reused.
237 * These can all be checked by looking at the value field.
239 if ( b
->v
!= NULL
) goto out_ab
;
242 * If this node is a copy, then we can do redundancy removal right now.
243 * This is an improvement over Manber and Ladner's work.
247 e
= weak_search(b
->l
, k
);
249 assert((e
== NULL
) || !IS_REDUNDANT(e
) || (e
->r
== b
));
251 redundancy_removal(ptst
, e
);
255 pac
= (a
->k
< k
) ? &a
->r
: &a
->l
;
259 if ( (b
->l
== NULL
) || (b
->r
== NULL
) )
261 if ( b
->r
== NULL
) *pac
= b
->l
; else *pac
= b
->r
;
263 if ( *pac
!= NULL
) (*pac
)->p
= a
;
264 gc_free(ptst
, b
, gc_id
);
269 e
= strong_search(b
->l
, b
->k
, &e_qn
);
270 assert(!IS_REDUNDANT(e
) && !IS_GARBAGE(e
) && (b
!= e
));
272 f
= gc_alloc(ptst
, gc_id
);
289 gc_free(ptst
, b
, gc_id
);
290 gc_add_ptr_to_hook_list(ptst
, e
, hook_id
);
301 set_t
*set_alloc(void)
305 s
= malloc(sizeof(*s
));
306 mcs_init(&s
->root
.lock
);
307 s
->root
.k
= SENTINEL_KEYMIN
;
308 /* Dummy root isn't redundant, nor is it garbage. */
309 s
->root
.v
= (setval_t
)(~3UL);
319 setval_t
set_update(set_t
*s
, setkey_t k
, setval_t v
, int overwrite
)
326 k
= CALLER_TO_INTERNAL_KEY(k
);
328 ptst
= critical_enter();
330 a
= strong_search(&s
->root
, k
, &qn
);
333 new = gc_alloc(ptst
, gc_id
);
334 mcs_init(&new->lock
);
341 if ( a
->k
< k
) a
->r
= new; else a
->l
= new;
345 /* Direct A->V access is okay, as A isn't garbage or redundant. */
347 if ( overwrite
|| (ov
== NULL
) ) a
->v
= v
;
358 setval_t
set_remove(set_t
*s
, setkey_t k
)
365 k
= CALLER_TO_INTERNAL_KEY(k
);
367 ptst
= critical_enter();
369 a
= strong_search(&s
->root
, k
, &qn
);
370 /* Direct check of A->V is okay, as A isn't garbage or redundant. */
371 if ( (a
->k
== k
) && (a
->v
!= NULL
) )
376 predecessor_substitution(ptst
, s
, a
);
389 setval_t
set_lookup(set_t
*s
, setkey_t k
)
395 k
= CALLER_TO_INTERNAL_KEY(k
);
397 ptst
= critical_enter();
399 n
= weak_search(&s
->root
, k
);
400 if ( n
!= NULL
) v
= GET_VALUE(n
);
407 void _init_set_subsystem(void)
409 gc_id
= gc_add_allocator(sizeof(node_t
));
410 hook_id
= gc_add_hook(redundancy_removal
);