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); }
/* Nodes p, x, y must be locked. */ static void right_rotate(ptst_t *ptst, node_t *x) { node_t *y = x->l, *p = x->p, *nx; nx = gc_alloc(ptst, gc_id); nx->p = y; nx->l = y->r; nx->r = x->r; nx->k = x->k; nx->v = x->v; mcs_init(&nx->lock); WMB(); y->p = p; x->r->p = nx; y->r->p = nx; y->r = nx; if ( x == p->l ) p->l = y; else p->r = y; MK_GARBAGE(x); gc_free(ptst, x, gc_id); }
/* 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); }
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); }