diff options
Diffstat (limited to 'nest/locks.c')
-rw-r--r-- | nest/locks.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/nest/locks.c b/nest/locks.c new file mode 100644 index 00000000..a2134fb6 --- /dev/null +++ b/nest/locks.c @@ -0,0 +1,147 @@ +/* + * BIRD Object Locks + * + * (c) 1999 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> + +#define LOCAL_DEBUG + +#include "nest/bird.h" +#include "lib/resource.h" +#include "nest/locks.h" +#include "nest/iface.h" + +static list olock_list; +static event *olock_event; + +static inline int +olock_same(struct object_lock *x, struct object_lock *y) +{ + return + x->type == y->type && + x->iface == y->iface && + x->port == y->port && + ipa_equal(x->addr, y->addr); +} + +static void +olock_free(resource *r) +{ + struct object_lock *q, *l = (struct object_lock *) r; + node *n; + + DBG("olock: Freeing %p\n", l); + switch (l->state) + { + case OLOCK_STATE_FREE: + break; + case OLOCK_STATE_LOCKED: + case OLOCK_STATE_EVENT: + rem_node(&l->n); + n = HEAD(l->waiters); + if (n->next) + { + DBG("olock: -> %p becomes locked\n", n); + q = SKIP_BACK(struct object_lock, n, n); + rem_node(n); + add_tail_list(&l->waiters, &q->waiters); + q->state = OLOCK_STATE_EVENT; + add_head(&olock_list, n); + ev_schedule(olock_event); + } + break; + case OLOCK_STATE_WAITING: + rem_node(&l->n); + break; + default: + ASSERT(0); + } +} + +static void +olock_dump(resource *r) +{ + struct object_lock *l = (struct object_lock *) r; + static char *olock_states[] = { "free", "locked", "waiting", "event" }; + + debug("(%d:%s:%I:%d) [%s]", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]); + if (!EMPTY_LIST(l->waiters)) + debug(" [wanted]\n"); +} + +static struct resclass olock_class = { + "ObjLock", + sizeof(struct object_lock), + olock_free, + olock_dump +}; + +struct object_lock * +olock_new(pool *p) +{ + struct object_lock *l = ralloc(p, &olock_class); + + l->state = OLOCK_STATE_FREE; + init_list(&l->waiters); + return l; +} + +void +olock_acquire(struct object_lock *l) +{ + node *n; + struct object_lock *q; + + WALK_LIST(n, olock_list) + { + q = SKIP_BACK(struct object_lock, n, n); + if (olock_same(q, l)) + { + l->state = OLOCK_STATE_WAITING; + add_tail(&q->waiters, &l->n); + DBG("olock: %p waits\n", l); + return; + } + } + DBG("olock: %p acquired immediately\n", l); + l->state = OLOCK_STATE_EVENT; + add_head(&olock_list, &l->n); + ev_schedule(olock_event); +} + +int +olock_run_event(void *unused) +{ + node *n; + struct object_lock *q; + + DBG("olock: Processing events\n"); + for(;;) + { + n = HEAD(olock_list); + if (!n->next) + break; + q = SKIP_BACK(struct object_lock, n, n); + if (q->state != OLOCK_STATE_EVENT) + break; + DBG("olock: %p locked\n", q); + q->state = OLOCK_STATE_LOCKED; + rem_node(&q->n); + add_tail(&olock_list, &q->n); + q->hook(q); + } + return 0; +} + +void +olock_init(void) +{ + DBG("olock: init\n"); + init_list(&olock_list); + olock_event = ev_new(&root_pool); + olock_event->hook = olock_run_event; +} |