static void redundancy_removal(ptst_t *ptst, void *x) { node_t *d, *e, *r; qnode_t d_qn, e_qn; setkey_t k; if ( x == NULL ) return; e = x; k = e->k; if ( e->copy ) { r = weak_search(e->l, k); assert((r == NULL) || !IS_REDUNDANT(r) || (r->r == e)); assert(r != e); redundancy_removal(ptst, r); } do { if ( IS_GARBAGE(e) ) return; d = e->p; LOCK(d, &d_qn); if ( IS_GARBAGE(d) ) UNLOCK(d, &d_qn); } while ( IS_GARBAGE(d) ); LOCK(e, &e_qn); if ( IS_GARBAGE(e) || !IS_REDUNDANT(e) ) goto out_de; if ( d->l == e ) { d->l = e->l; } else { assert(d->r == e); d->r = e->l; } assert(e->r != NULL); assert(e->r->k == k); assert(e->r->copy); assert(!IS_GARBAGE(e->r)); assert(!e->copy); MK_GARBAGE(e); if ( e->l != NULL ) e->l->p = d; e->r->copy = 0; gc_free(ptst, e, gc_id); out_de: UNLOCK(d, &d_qn); UNLOCK(e, &e_qn); }
static node_t *strong_search(node_t *n, setkey_t k, qnode_t *qn) { node_t *b = n; node_t *a = FOLLOW(b, k); retry: while ( (a != NULL) && (a->k != k) ) { b = a; a = FOLLOW(a, k); } if ( a == NULL ) { LOCK(b, qn); if ( IS_GARBAGE(b) ) { UNLOCK(b, qn); a = b->p; goto retry; } else if ( (a = FOLLOW(b, k)) != NULL ) { UNLOCK(b, qn); goto retry; } a = b; } else { LOCK(a, qn); if ( IS_GARBAGE(a) ) { UNLOCK(a, qn); a = a->p; goto retry; } else if ( IS_REDUNDANT(a) ) { UNLOCK(a, qn); a = a->r; goto retry; } } return a; }
setval_t set_remove(set_t *s, setkey_t k) { ptst_t *ptst; node_t *y, *z; qnode_t z_qn; setval_t ov = NULL; k = CALLER_TO_INTERNAL_KEY(k); ptst = critical_enter(); z = &s->root; while ( (y = (k <= z->k) ? z->l : z->r) != NULL ) z = y; if ( z->k == k ) { mcs_lock(&z->lock, &z_qn); if ( !IS_GARBAGE(z) ) { ov = GET_VALUE(z->v); SET_VALUE(z->v, NULL); } mcs_unlock(&z->lock, &z_qn); } if ( ov != NULL ) delete_finish(ptst, z); critical_exit(ptst); return ov; }
/* NB. Node X is not locked on entry. */ static void predecessor_substitution(ptst_t *ptst, set_t *s, node_t *x) { node_t *a, *b, *e, *f, **pac; qnode_t a_qn, b_qn, e_qn, f_qn; setkey_t k; b = x; k = x->k; do { if ( (b == NULL) || (b->v != NULL) ) return; a = b->p; LOCK(a, &a_qn); if ( IS_GARBAGE(a) ) UNLOCK(a, &a_qn); } while ( IS_GARBAGE(a) ); regain_lock: LOCK(b, &b_qn); /* * We do nothing if: * 1. The node is already deleted (and is thus garbage); or * 2. The node is redundant (redundancy removal will do it); or * 3. The node has been reused. * These can all be checked by looking at the value field. */ if ( b->v != NULL ) goto out_ab; /* * If this node is a copy, then we can do redundancy removal right now. * This is an improvement over Manber and Ladner's work. */ if ( b->copy ) { e = weak_search(b->l, k); UNLOCK(b, &b_qn); assert((e == NULL) || !IS_REDUNDANT(e) || (e->r == b)); assert(e != b); redundancy_removal(ptst, e); goto regain_lock; } pac = (a->k < k) ? &a->r : &a->l; assert(*pac == b); assert(b->p == a); if ( (b->l == NULL) || (b->r == NULL) ) { if ( b->r == NULL ) *pac = b->l; else *pac = b->r; MK_GARBAGE(b); if ( *pac != NULL ) (*pac)->p = a; gc_free(ptst, b, gc_id); goto out_ab; } else { e = strong_search(b->l, b->k, &e_qn); assert(!IS_REDUNDANT(e) && !IS_GARBAGE(e) && (b != e)); assert(e->k < b->k); f = gc_alloc(ptst, gc_id); f->k = e->k; f->v = GET_VALUE(e); f->copy = 1; f->r = b->r; f->l = b->l; mcs_init(&f->lock); LOCK(f, &f_qn); e->r = f; MK_REDUNDANT(e); *pac = f; f->p = a; f->r->p = f; f->l->p = f; MK_GARBAGE(b); gc_free(ptst, b, gc_id); gc_add_ptr_to_hook_list(ptst, e, hook_id); UNLOCK(e, &e_qn); UNLOCK(f, &f_qn); } out_ab: UNLOCK(a, &a_qn); UNLOCK(b, &b_qn); }
setval_t set_update(set_t *s, setkey_t k, setval_t v, int overwrite) { ptst_t *ptst; qnode_t y_qn, z_qn; node_t *y, *z, *new_internal, *new_leaf; int fix_up = 0; setval_t ov = NULL; k = CALLER_TO_INTERNAL_KEY(k); ptst = critical_enter(); retry: z = &s->root; while ( (y = (k <= z->k) ? z->l : z->r) != NULL ) z = y; y = z->p; mcs_lock(&y->lock, &y_qn); if ( (((k <= y->k) ? y->l : y->r) != z) || IS_GARBAGE(y) ) { mcs_unlock(&y->lock, &y_qn); goto retry; } mcs_lock(&z->lock, &z_qn); assert(!IS_GARBAGE(z) && IS_LEAF(z)); if ( z->k == k ) { ov = GET_VALUE(z->v); if ( overwrite || (ov == NULL) ) SET_VALUE(z->v, v); } else { new_leaf = gc_alloc(ptst, gc_id); new_internal = gc_alloc(ptst, gc_id); new_leaf->k = k; new_leaf->v = MK_BLACK(v); new_leaf->l = NULL; new_leaf->r = NULL; new_leaf->p = new_internal; mcs_init(&new_leaf->lock); if ( z->k < k ) { new_internal->k = z->k; new_internal->l = z; new_internal->r = new_leaf; } else { new_internal->k = k; new_internal->l = new_leaf; new_internal->r = z; } new_internal->p = y; mcs_init(&new_internal->lock); if ( IS_UNBALANCED(z->v) ) { z->v = MK_BALANCED(z->v); new_internal->v = MK_BLACK(INTERNAL_VALUE); } else if ( IS_RED(y->v) ) { new_internal->v = MK_UNBALANCED(MK_RED(INTERNAL_VALUE)); fix_up = 1; } else { new_internal->v = MK_RED(INTERNAL_VALUE); } WMB(); z->p = new_internal; if ( y->l == z ) y->l = new_internal; else y->r = new_internal; } mcs_unlock(&y->lock, &y_qn); mcs_unlock(&z->lock, &z_qn); if ( fix_up ) fix_unbalance_up(ptst, new_internal); out: critical_exit(ptst); return ov; }
static void delete_finish(ptst_t *ptst, node_t *x) { qnode_t g_qn, p_qn, w_qn, x_qn; node_t *g, *p, *w; int done = 0; do { if ( IS_GARBAGE(x) ) return; p = x->p; g = p->p; mcs_lock(&g->lock, &g_qn); if ( !ADJACENT(g, p) || IS_UNBALANCED(g->v) || IS_GARBAGE(g) ) goto unlock_g; mcs_lock(&p->lock, &p_qn); /* Removing unbalanced red nodes is okay. */ if ( !ADJACENT(p, x) || (IS_UNBALANCED(p->v) && IS_BLACK(p->v)) ) goto unlock_pg; mcs_lock(&x->lock, &x_qn); if ( IS_UNBALANCED(x->v) ) goto unlock_xpg; if ( GET_VALUE(x->v) != NULL ) { done = 1; goto unlock_xpg; } if ( p->l == x ) w = p->r; else w = p->l; assert(w != x); mcs_lock(&w->lock, &w_qn); if ( IS_UNBALANCED(w->v) ) goto unlock_wxpg; if ( g->l == p ) g->l = w; else g->r = w; MK_GARBAGE(p); gc_free(ptst, p, gc_id); MK_GARBAGE(x); gc_free(ptst, x, gc_id); w->p = g; if ( IS_BLACK(p->v) && IS_BLACK(w->v) ) { w->v = MK_UNBALANCED(w->v); done = 2; } else { w->v = MK_BLACK(w->v); done = 1; } unlock_wxpg: mcs_unlock(&w->lock, &w_qn); unlock_xpg: mcs_unlock(&x->lock, &x_qn); unlock_pg: mcs_unlock(&p->lock, &p_qn); unlock_g: mcs_unlock(&g->lock, &g_qn); } while ( !done ); if ( done == 2 ) fix_unbalance_down(ptst, w); }
static void fix_unbalance_down(ptst_t *ptst, node_t *x) { /* WN == W_NEAR, WF == W_FAR (W_FAR is further, in key space, from X). */ qnode_t x_qn, w_qn, p_qn, g_qn, wn_qn, wf_qn; node_t *w, *p, *g, *wn, *wf; int done = 0; do { if ( !IS_UNBALANCED(x->v) || IS_GARBAGE(x) ) return; p = x->p; g = p->p; mcs_lock(&g->lock, &g_qn); if ( !ADJACENT(g, p) || IS_UNBALANCED(g->v) || IS_GARBAGE(g) ) goto unlock_g; mcs_lock(&p->lock, &p_qn); if ( !ADJACENT(p, x) || IS_UNBALANCED(p->v) ) goto unlock_pg; mcs_lock(&x->lock, &x_qn); if ( !IS_BLACK(x->v) || !IS_UNBALANCED(x->v) ) { done = 1; goto unlock_xpg; } if ( IS_ROOT(x) ) { x->v = MK_BALANCED(x->v); done = 1; goto unlock_xpg; } w = (x == p->l) ? p->r : p->l; mcs_lock(&w->lock, &w_qn); if ( IS_UNBALANCED(w->v) ) { if ( IS_BLACK(w->v) ) { /* Funky relaxed rules to the rescue. */ x->v = MK_BALANCED(x->v); w->v = MK_BALANCED(w->v); if ( IS_BLACK(p->v) ) { p->v = MK_UNBALANCED(p->v); done = 2; } else { p->v = MK_BLACK(p->v); done = 1; } } goto unlock_wxpg; } assert(!IS_LEAF(w)); if ( x == p->l ) { wn = w->l; wf = w->r; } else { wn = w->r; wf = w->l; } mcs_lock(&wn->lock, &wn_qn); /* Hanke has an extra relaxed transform here. It's not needed. */ if ( IS_UNBALANCED(wn->v) ) goto unlock_wnwxpg; mcs_lock(&wf->lock, &wf_qn); if ( IS_UNBALANCED(wf->v) ) goto unlock_wfwnwxpg; if ( IS_RED(w->v) ) { /* Case 1. Rotate at parent. */ assert(IS_BLACK(p->v) && IS_BLACK(wn->v) && IS_BLACK(wf->v)); w->v = MK_BLACK(w->v); p->v = MK_RED(p->v); if ( x == p->l ) left_rotate(ptst, p); else right_rotate(ptst, p); goto unlock_wfwnwxpg; } if ( IS_BLACK(wn->v) && IS_BLACK(wf->v) ) { if ( IS_RED(p->v) ) { /* Case 2. Simple recolouring. */ p->v = MK_BLACK(p->v); done = 1; } else { /* Case 5. Simple recolouring. */ p->v = MK_UNBALANCED(p->v); done = 2; } w->v = MK_RED(w->v); x->v = MK_BALANCED(x->v); goto unlock_wfwnwxpg; } if ( x == p->l ) { if ( IS_RED(wf->v) ) { /* Case 3. Single rotation. */ wf->v = MK_BLACK(wf->v); w->v = SET_COLOUR(w->v, GET_COLOUR(p->v)); p->v = MK_BLACK(p->v); x->v = MK_BALANCED(x->v); left_rotate(ptst, p); } else { /* Case 4. Double rotation. */ assert(IS_RED(wn->v)); wn->v = SET_COLOUR(wn->v, GET_COLOUR(p->v)); p->v = MK_BLACK(p->v); x->v = MK_BALANCED(x->v); right_rotate(ptst, w); left_rotate(ptst, p); } } else /* SYMMETRIC CASE: X == P->R */ { if ( IS_RED(wf->v) ) { /* Case 3. Single rotation. */ wf->v = MK_BLACK(wf->v); w->v = SET_COLOUR(w->v, GET_COLOUR(p->v)); p->v = MK_BLACK(p->v); x->v = MK_BALANCED(x->v); right_rotate(ptst, p); } else { /* Case 4. Double rotation. */ assert(IS_RED(wn->v)); wn->v = SET_COLOUR(wn->v, GET_COLOUR(p->v)); p->v = MK_BLACK(p->v); x->v = MK_BALANCED(x->v); left_rotate(ptst, w); right_rotate(ptst, p); } } done = 1; unlock_wfwnwxpg: mcs_unlock(&wf->lock, &wf_qn); unlock_wnwxpg: mcs_unlock(&wn->lock, &wn_qn); unlock_wxpg: mcs_unlock(&w->lock, &w_qn); unlock_xpg: mcs_unlock(&x->lock, &x_qn); unlock_pg: mcs_unlock(&p->lock, &p_qn); unlock_g: mcs_unlock(&g->lock, &g_qn); if ( done == 2 ) { x = p; done = 0; } } while ( !done ); }
static void fix_unbalance_up(ptst_t *ptst, node_t *x) { qnode_t x_qn, g_qn, p_qn, w_qn, gg_qn; node_t *g, *p, *w, *gg; int done = 0; do { assert(IS_UNBALANCED(x->v)); if ( IS_GARBAGE(x) ) return; p = x->p; g = p->p; gg = g->p; mcs_lock(&gg->lock, &gg_qn); if ( !ADJACENT(gg, g) || IS_UNBALANCED(gg->v) || IS_GARBAGE(gg) ) goto unlock_gg; mcs_lock(&g->lock, &g_qn); if ( !ADJACENT(g, p) || IS_UNBALANCED(g->v) ) goto unlock_ggg; mcs_lock(&p->lock, &p_qn); if ( !ADJACENT(p, x) || IS_UNBALANCED(p->v) ) goto unlock_pggg; mcs_lock(&x->lock, &x_qn); assert(IS_RED(x->v)); assert(IS_UNBALANCED(x->v)); if ( IS_BLACK(p->v) ) { /* Case 1. Nothing to do. */ x->v = MK_BALANCED(x->v); done = 1; goto unlock_xpggg; } if ( IS_ROOT(x) ) { /* Case 2. */ x->v = MK_BLACK(MK_BALANCED(x->v)); done = 1; goto unlock_xpggg; } if ( IS_ROOT(p) ) { /* Case 2. */ p->v = MK_BLACK(p->v); x->v = MK_BALANCED(x->v); done = 1; goto unlock_xpggg; } if ( g->l == p ) w = g->r; else w = g->l; mcs_lock(&w->lock, &w_qn); if ( IS_RED(w->v) ) { /* Case 5. */ /* In all other cases, doesn't change colour or subtrees. */ if ( IS_UNBALANCED(w->v) ) goto unlock_wxpggg; g->v = MK_UNBALANCED(MK_RED(g->v)); p->v = MK_BLACK(p->v); w->v = MK_BLACK(w->v); x->v = MK_BALANCED(x->v); done = 2; goto unlock_wxpggg; } /* Cases 3 & 4. Both of these need the great-grandfather locked. */ if ( p == g->l ) { if ( x == p->l ) { /* Case 3. Single rotation. */ x->v = MK_BALANCED(x->v); p->v = MK_BLACK(p->v); g->v = MK_RED(g->v); right_rotate(ptst, g); } else { /* Case 4. Double rotation. */ x->v = MK_BALANCED(MK_BLACK(x->v)); g->v = MK_RED(g->v); left_rotate(ptst, p); right_rotate(ptst, g); } } else /* SYMMETRIC CASE */ { if ( x == p->r ) { /* Case 3. Single rotation. */ x->v = MK_BALANCED(x->v); p->v = MK_BLACK(p->v); g->v = MK_RED(g->v); left_rotate(ptst, g); } else { /* Case 4. Double rotation. */ x->v = MK_BALANCED(MK_BLACK(x->v)); g->v = MK_RED(g->v); right_rotate(ptst, p); left_rotate(ptst, g); } } done = 1; unlock_wxpggg: mcs_unlock(&w->lock, &w_qn); unlock_xpggg: mcs_unlock(&x->lock, &x_qn); unlock_pggg: mcs_unlock(&p->lock, &p_qn); unlock_ggg: mcs_unlock(&g->lock, &g_qn); unlock_gg: mcs_unlock(&gg->lock, &gg_qn); if ( done == 2 ) { x = g; done = 0; } } while ( !done ); }