Esempio n. 1
0
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);
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
/* 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);
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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);
}
Esempio n. 7
0
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 );
}
Esempio n. 8
0
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 );
}