/* * BIRD Object Locks * * (c) 1999 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #undef 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]\n", 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); } static void 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); } } void olock_init(void) { DBG("olock: init\n"); init_list(&olock_list); olock_event = ev_new(&root_pool); olock_event->hook = olock_run_event; }