summaryrefslogtreecommitdiffstats
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/Makefile2
-rw-r--r--nest/locks.c147
-rw-r--r--nest/locks.h52
3 files changed, 200 insertions, 1 deletions
diff --git a/nest/Makefile b/nest/Makefile
index 5b6b414..69bc06c 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 0000000..a2134fb
--- /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;
+}
diff --git a/nest/locks.h b/nest/locks.h
new file mode 100644
index 0000000..e465bf5
--- /dev/null
+++ b/nest/locks.h
@@ -0,0 +1,52 @@
+/*
+ * BIRD Object Locks
+ *
+ * (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ * 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