Implemented deletion/insertion/asynchronous-walk lists.
For example of their use, look at comments in lib/slists.h.
This commit is contained in:
parent
29ad2c9ee1
commit
a05406e69c
3 changed files with 321 additions and 0 deletions
|
@ -20,3 +20,5 @@ xmalloc.c
|
||||||
printf.c
|
printf.c
|
||||||
string.h
|
string.h
|
||||||
patmatch.c
|
patmatch.c
|
||||||
|
slists.c
|
||||||
|
slists.h
|
||||||
|
|
231
lib/slists.c
Normal file
231
lib/slists.c
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* BIRD Library -- Safe Linked Lists
|
||||||
|
*
|
||||||
|
* (c) 1998 Martin Mares <mj@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _BIRD_SLISTS_C_
|
||||||
|
|
||||||
|
#include "nest/bird.h"
|
||||||
|
#include "lib/slists.h"
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
s_merge(snode *from, snode *to)
|
||||||
|
{
|
||||||
|
siterator *f, *g;
|
||||||
|
|
||||||
|
if (!(f = from->readers))
|
||||||
|
return;
|
||||||
|
if (!(g = to->readers))
|
||||||
|
{
|
||||||
|
/* Fast path */
|
||||||
|
to->readers = f;
|
||||||
|
f->prev = (siterator *) to;
|
||||||
|
fixup:
|
||||||
|
while (f && f->node)
|
||||||
|
{
|
||||||
|
f->node = NULL;
|
||||||
|
f = f->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Really merging */
|
||||||
|
while (g->next)
|
||||||
|
g = g->next;
|
||||||
|
g->next = f;
|
||||||
|
f->prev = g;
|
||||||
|
goto fixup;
|
||||||
|
}
|
||||||
|
|
||||||
|
snode *
|
||||||
|
s_get(siterator *i)
|
||||||
|
{
|
||||||
|
siterator *f, *g;
|
||||||
|
snode *n;
|
||||||
|
|
||||||
|
if (!(n = i->node))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No node found. We have to walk the iterator list backwards
|
||||||
|
* to find where are we linked.
|
||||||
|
*/
|
||||||
|
f = i;
|
||||||
|
while (!f->null)
|
||||||
|
f = f->prev;
|
||||||
|
n = (snode *) f;
|
||||||
|
}
|
||||||
|
f = i->prev; /* Maybe the snode itself */
|
||||||
|
g = i->next;
|
||||||
|
f->next = g;
|
||||||
|
if (g)
|
||||||
|
g->prev = f;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_put(siterator *i, snode *n)
|
||||||
|
{
|
||||||
|
siterator *f;
|
||||||
|
|
||||||
|
i->node = n;
|
||||||
|
if (f = n->readers)
|
||||||
|
f->prev = i;
|
||||||
|
i->next = f;
|
||||||
|
n->readers = i;
|
||||||
|
i->prev = (siterator *) n;
|
||||||
|
i->null = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_add_tail(slist *l, snode *n)
|
||||||
|
{
|
||||||
|
snode *z = l->tail;
|
||||||
|
|
||||||
|
n->next = (snode *) &l->null;
|
||||||
|
n->prev = z;
|
||||||
|
z->next = n;
|
||||||
|
l->tail = n;
|
||||||
|
n->readers = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_add_head(slist *l, snode *n)
|
||||||
|
{
|
||||||
|
snode *z = l->head;
|
||||||
|
|
||||||
|
n->next = z;
|
||||||
|
n->prev = (snode *) &l->head;
|
||||||
|
z->prev = n;
|
||||||
|
l->head = n;
|
||||||
|
n->readers = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_insert_node(snode *n, snode *after)
|
||||||
|
{
|
||||||
|
snode *z = after->next;
|
||||||
|
|
||||||
|
n->next = z;
|
||||||
|
n->prev = after;
|
||||||
|
after->next = n;
|
||||||
|
z->prev = n;
|
||||||
|
n->readers = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_rem_node(snode *n)
|
||||||
|
{
|
||||||
|
snode *z = n->prev;
|
||||||
|
snode *x = n->next;
|
||||||
|
|
||||||
|
z->next = x;
|
||||||
|
x->prev = z;
|
||||||
|
s_merge(n, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_init_list(slist *l)
|
||||||
|
{
|
||||||
|
l->head = (snode *) &l->null;
|
||||||
|
l->null = NULL;
|
||||||
|
l->tail = (snode *) &l->head;
|
||||||
|
l->tail_readers = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
s_add_tail_list(slist *to, slist *l)
|
||||||
|
{
|
||||||
|
snode *p = to->tail;
|
||||||
|
snode *q = l->head;
|
||||||
|
|
||||||
|
p->next = q;
|
||||||
|
q->prev = p;
|
||||||
|
q = l->tail;
|
||||||
|
q->next = (snode *) &to->null;
|
||||||
|
to->tail = q;
|
||||||
|
s_merge((snode *) &l->null, (snode *) &to->null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
|
||||||
|
#include "lib/resource.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void dump(char *c, slist *a)
|
||||||
|
{
|
||||||
|
snode *x;
|
||||||
|
|
||||||
|
puts(c);
|
||||||
|
for(x=SHEAD(*a); x; x=x->next)
|
||||||
|
{
|
||||||
|
siterator *i, *j;
|
||||||
|
printf("%p", x);
|
||||||
|
j = (siterator *) x;
|
||||||
|
for(i=x->readers; i; i=i->next)
|
||||||
|
{
|
||||||
|
if (i->prev != j)
|
||||||
|
printf(" ???");
|
||||||
|
j = i;
|
||||||
|
printf(" [%p:%p]", i, i->node);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
puts("---");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
slist a, b;
|
||||||
|
snode *x, *y;
|
||||||
|
siterator i, j;
|
||||||
|
|
||||||
|
s_init_list(&a);
|
||||||
|
s_init_list(&b);
|
||||||
|
x = xmalloc(sizeof(*x));
|
||||||
|
s_add_tail(&a, x);
|
||||||
|
x = xmalloc(sizeof(*x));
|
||||||
|
s_add_tail(&a, x);
|
||||||
|
x = xmalloc(sizeof(*x));
|
||||||
|
s_add_tail(&a, x);
|
||||||
|
dump("1", &a);
|
||||||
|
|
||||||
|
s_init(&i, &a);
|
||||||
|
s_init(&j, &a);
|
||||||
|
dump("2", &a);
|
||||||
|
|
||||||
|
x = s_get(&i);
|
||||||
|
printf("Got %p\n", x);
|
||||||
|
dump("3", &a);
|
||||||
|
|
||||||
|
s_put(&i, x->next);
|
||||||
|
dump("4", &a);
|
||||||
|
|
||||||
|
y = s_get(&j);
|
||||||
|
while (y)
|
||||||
|
{
|
||||||
|
s_put(&j, y);
|
||||||
|
dump("5*", &a);
|
||||||
|
y = s_get(&j)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump("5 done", &a);
|
||||||
|
|
||||||
|
s_rem_node(a.head->next);
|
||||||
|
dump("6 (deletion)", &a);
|
||||||
|
|
||||||
|
s_put(&i, s_get(&i)->next);
|
||||||
|
dump("6 (relink)", &a);
|
||||||
|
|
||||||
|
x = xmalloc(sizeof(*x));
|
||||||
|
s_add_tail(&b, x);
|
||||||
|
dump("7 (second list)", &b);
|
||||||
|
|
||||||
|
s_add_tail_list(&b, &a);
|
||||||
|
dump("8 (after merge)", &b);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
88
lib/slists.h
Normal file
88
lib/slists.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* BIRD Library -- Safe Linked Lists
|
||||||
|
*
|
||||||
|
* (c) 1998 Martin Mares <mj@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_SLISTS_H_
|
||||||
|
#define _BIRD_SLISTS_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These linked lists work in a way similar to standard lists defined
|
||||||
|
* in lib/lists.h, but in addition to all usual list functions they
|
||||||
|
* provide fast deletion/insertion/everything-safe asynchronous
|
||||||
|
* walking.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* slist l;
|
||||||
|
* siterator i;
|
||||||
|
* snode *n;
|
||||||
|
*
|
||||||
|
* s_init(&i, &l); // Initialize iteration
|
||||||
|
* ...
|
||||||
|
* n = s_get(&i); // Some time later, fetch present
|
||||||
|
* // value of the iterator and unlink it
|
||||||
|
* // from the list.
|
||||||
|
* while (n->next) {
|
||||||
|
* ...
|
||||||
|
* if (decided_to_stop) {
|
||||||
|
* s_put(&i, n); // Store current position (maybe even
|
||||||
|
* // that we stay at list end)
|
||||||
|
* return; // and return
|
||||||
|
* }
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* // After finishing, don't link the iterator back
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct snode {
|
||||||
|
struct snode *next, *prev;
|
||||||
|
struct siterator *readers;
|
||||||
|
} snode;
|
||||||
|
|
||||||
|
typedef struct slist { /* In fact two overlayed snodes */
|
||||||
|
struct snode *head, *null, *tail;
|
||||||
|
struct siterator *tail_readers;
|
||||||
|
} slist;
|
||||||
|
|
||||||
|
typedef struct siterator {
|
||||||
|
/*
|
||||||
|
* Caution: Layout of this structure depends hard on layout of the
|
||||||
|
* snode. Our `next' must be at position of snode `readers'
|
||||||
|
* field, our `null' must be at position of `prev' and it must
|
||||||
|
* contain NULL in order to distinguish between siterator
|
||||||
|
* and snode (snodes with NULL `prev' field never carry
|
||||||
|
* iterators). You are not expected to understand this.
|
||||||
|
*/
|
||||||
|
struct siterator *prev, *null, *next;
|
||||||
|
/*
|
||||||
|
* For recently merged nodes this can be NULL, but then it's NULL
|
||||||
|
* for all successors as well. This is done to speed up iterator
|
||||||
|
* merging when there are lots of deletions.
|
||||||
|
*/
|
||||||
|
snode *node;
|
||||||
|
} siterator;
|
||||||
|
|
||||||
|
#define SNODE (snode *)
|
||||||
|
#define SHEAD(list) ((void *)((list).head))
|
||||||
|
#define STAIL(list) ((void *)((list).tail))
|
||||||
|
#define WALK_SLIST(n,list) for(n=SHEAD(list);(SNODE (n))->next; \
|
||||||
|
n=(void *)((SNODE (n))->next))
|
||||||
|
#define WALK_SLIST_DELSAFE(n,nxt,list) \
|
||||||
|
for(n=SHEAD(list); nxt=(void *)((SNODE (n))->next); n=(void *) nxt)
|
||||||
|
#define EMPTY_SLIST(list) (!(list).head->next)
|
||||||
|
|
||||||
|
void s_add_tail(slist *, snode *);
|
||||||
|
void s_add_head(slist *, snode *);
|
||||||
|
void s_rem_node(snode *);
|
||||||
|
void s_add_tail_list(slist *, slist *);
|
||||||
|
void s_init_list(slist *);
|
||||||
|
void s_insert_node(snode *, snode *);
|
||||||
|
|
||||||
|
snode *s_get(siterator *);
|
||||||
|
void s_put(siterator *, snode *n);
|
||||||
|
static inline void s_init(siterator *i, slist *l) { s_put(i, SHEAD(*l)); }
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue