]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/locks.c
Documented.
[thirdparty/bird.git] / nest / locks.c
CommitLineData
f545d387
MM
1/*
2 * BIRD Object Locks
3 *
4 * (c) 1999 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
1f495723
MM
9/**
10 * DOC: Object locks
11 *
12 * The lock module provides a simple mechanism for avoiding conflicts between
13 * various protocols which would like to use a single physical resource (for
14 * example a network port). It would be easy to say that such collisions can
15 * occur only when the user specifies an invalid configuration and therefore
16 * he deserves to get what he has asked for, but unfortunately they can also
17 * arise legitimately when the daemon is reconfigured and there exists (although
18 * for a short time period only) an old protocol being shut down and a new one
19 * willing to start up on the same interface.
20 *
21 * The solution is very simple: when any protocol wishes to use a network port
22 * or some other non-shareable resource, it asks the core to lock it and doesn't
23 * use the resource until it's notified that it has acquired the lock.
24 *
25 * Object locks are represented by &object_lock which is in turn a kind of
26 * resource. Lockable resources are uniquely determined by resource type
27 * (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or
28 * multicast address the port is bound to), port number and interface.
29 */
30
1d9622e1 31#undef LOCAL_DEBUG
f545d387
MM
32
33#include "nest/bird.h"
34#include "lib/resource.h"
35#include "nest/locks.h"
36#include "nest/iface.h"
37
38static list olock_list;
39static event *olock_event;
40
41static inline int
42olock_same(struct object_lock *x, struct object_lock *y)
43{
44 return
45 x->type == y->type &&
46 x->iface == y->iface &&
47 x->port == y->port &&
48 ipa_equal(x->addr, y->addr);
49}
50
51static void
52olock_free(resource *r)
53{
54 struct object_lock *q, *l = (struct object_lock *) r;
55 node *n;
56
57 DBG("olock: Freeing %p\n", l);
58 switch (l->state)
59 {
60 case OLOCK_STATE_FREE:
61 break;
62 case OLOCK_STATE_LOCKED:
63 case OLOCK_STATE_EVENT:
64 rem_node(&l->n);
65 n = HEAD(l->waiters);
66 if (n->next)
67 {
68 DBG("olock: -> %p becomes locked\n", n);
69 q = SKIP_BACK(struct object_lock, n, n);
70 rem_node(n);
71 add_tail_list(&l->waiters, &q->waiters);
72 q->state = OLOCK_STATE_EVENT;
73 add_head(&olock_list, n);
74 ev_schedule(olock_event);
75 }
76 break;
77 case OLOCK_STATE_WAITING:
78 rem_node(&l->n);
79 break;
80 default:
81 ASSERT(0);
82 }
83}
84
85static void
86olock_dump(resource *r)
87{
88 struct object_lock *l = (struct object_lock *) r;
89 static char *olock_states[] = { "free", "locked", "waiting", "event" };
90
267a2c0e 91 debug("(%d:%s:%I:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]);
f545d387
MM
92 if (!EMPTY_LIST(l->waiters))
93 debug(" [wanted]\n");
94}
95
96static struct resclass olock_class = {
97 "ObjLock",
98 sizeof(struct object_lock),
99 olock_free,
100 olock_dump
101};
102
1f495723
MM
103/**
104 * olock_new - create an object lock
105 * @p: resource pool to create the lock in.
106 *
107 * The olock_new() function creates a new resource of type &object_lock
108 * and returns a pointer to it. After filling in the structure, the caller
109 * should call olock_acquire() to do the real locking.
110 */
f545d387
MM
111struct object_lock *
112olock_new(pool *p)
113{
114 struct object_lock *l = ralloc(p, &olock_class);
115
116 l->state = OLOCK_STATE_FREE;
117 init_list(&l->waiters);
118 return l;
119}
120
1f495723
MM
121/**
122 * olock_acquire - acquire a lock
123 * @l: the lock to acquire
124 *
125 * This function attempts to acquire exclusive access to the non-shareable
126 * resource described by the lock @l. It returns immediately, but as soon
127 * as the resource becomes available, it calls the hook() function set up
128 * by the caller.
129 *
130 * When you want to release the resource, just rfree() the lock.
131 */
f545d387
MM
132void
133olock_acquire(struct object_lock *l)
134{
135 node *n;
136 struct object_lock *q;
137
138 WALK_LIST(n, olock_list)
139 {
140 q = SKIP_BACK(struct object_lock, n, n);
141 if (olock_same(q, l))
142 {
143 l->state = OLOCK_STATE_WAITING;
144 add_tail(&q->waiters, &l->n);
145 DBG("olock: %p waits\n", l);
146 return;
147 }
148 }
149 DBG("olock: %p acquired immediately\n", l);
150 l->state = OLOCK_STATE_EVENT;
151 add_head(&olock_list, &l->n);
152 ev_schedule(olock_event);
153}
154
8f6accb5 155static void
f545d387
MM
156olock_run_event(void *unused)
157{
158 node *n;
159 struct object_lock *q;
160
161 DBG("olock: Processing events\n");
162 for(;;)
163 {
164 n = HEAD(olock_list);
165 if (!n->next)
166 break;
167 q = SKIP_BACK(struct object_lock, n, n);
168 if (q->state != OLOCK_STATE_EVENT)
169 break;
170 DBG("olock: %p locked\n", q);
171 q->state = OLOCK_STATE_LOCKED;
172 rem_node(&q->n);
173 add_tail(&olock_list, &q->n);
174 q->hook(q);
175 }
f545d387
MM
176}
177
1f495723
MM
178/**
179 * olock_init - initialize the object lock mechanism
180 *
181 * This function is called during BIRD startup. It initializes
182 * all the internal data structures of the lock module.
183 */
f545d387
MM
184void
185olock_init(void)
186{
187 DBG("olock: init\n");
188 init_list(&olock_list);
189 olock_event = ev_new(&root_pool);
190 olock_event->hook = olock_run_event;
191}