diff --git a/nest/Makefile b/nest/Makefile index 5b6b414e..69bc06c3 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -1,4 +1,4 @@ -source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c +source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c locks.c root-rel=../ dir-name=nest 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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include + +#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; +} diff --git a/nest/locks.h b/nest/locks.h new file mode 100644 index 00000000..e465bf55 --- /dev/null +++ b/nest/locks.h @@ -0,0 +1,52 @@ +/* + * BIRD Object Locks + * + * (c) 1999 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_LOCKS_H_ +#define _BIRD_LOCKS_H_ + +#include "lib/resource.h" +#include "lib/event.h" + +/* + * The object locks are used for controlling exclusive access + * to various physical resources like UDP ports on specific devices. + * When you want to access such resource, you ask for a object lock + * structure, fill in specification of the object and your function + * you want to have called when the object is available and invoke + * olock_acquire() afterwards. When the object becomes free, the lock + * manager calls your function. To free the object lock, just call rfree + * on its resource. + */ + +struct object_lock { + resource r; + ip_addr addr; /* Identification of a object: IP address */ + unsigned int type; /* ... object type (OBJLOCK_xxx) */ + struct iface *iface; /* ... interface */ + unsigned int port; /* ... port number */ + void (*hook)(struct object_lock *); /* Called when the lock succeeds */ + void *data; /* User data */ + /* ... internal to lock manager, don't touch ... */ + node n; /* Node in list of olocks */ + int state; /* OLOCK_STATE_xxx */ + list waiters; /* Locks waiting for the same resource */ +}; + +struct object_lock *olock_new(pool *); +void olock_acquire(struct object_lock *); +void olock_init(void); + +#define OBJLOCK_UDP 1 /* UDP port */ +#define OBJLOCK_TCP 2 /* TCP port */ + +#define OLOCK_STATE_FREE 0 +#define OLOCK_STATE_LOCKED 1 +#define OLOCK_STATE_WAITING 2 +#define OLOCK_STATE_EVENT 3 /* waiting for unlock processing */ + +#endif diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 5a49dbd4..4b9ab615 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -22,6 +22,7 @@ #include "nest/protocol.h" #include "nest/iface.h" #include "nest/cli.h" +#include "nest/locks.h" #include "conf/conf.h" #include "filter/filter.h" @@ -334,6 +335,7 @@ main(int argc, char **argv) debug("Initializing.\n"); resource_init(); + olock_init(); io_init(); rt_init(); if_init();