unixctl: New JSON RPC back-end.
[openvswitch] / ovsdb / server.c
1 /* Copyright (c) 2011 Nicira Networks
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include "server.h"
19
20 #include <assert.h>
21
22 #include "hash.h"
23
24 /* Initializes 'session' as a session that operates on 'db'. */
25 void
26 ovsdb_session_init(struct ovsdb_session *session, struct ovsdb *db)
27 {
28     session->db = db;
29     list_init(&session->completions);
30     hmap_init(&session->waiters);
31 }
32
33 /* Destroys 'session'. */
34 void
35 ovsdb_session_destroy(struct ovsdb_session *session)
36 {
37     assert(hmap_is_empty(&session->waiters));
38     hmap_destroy(&session->waiters);
39 }
40
41 /* Searches 'session' for an ovsdb_lock_waiter named 'lock_name' and returns
42  * it if it finds one, otherwise NULL. */
43 struct ovsdb_lock_waiter *
44 ovsdb_session_get_lock_waiter(const struct ovsdb_session *session,
45                               const char *lock_name)
46 {
47     struct ovsdb_lock_waiter *waiter;
48
49     HMAP_FOR_EACH_WITH_HASH (waiter, session_node, hash_string(lock_name, 0),
50                              &session->waiters) {
51         if (!strcmp(lock_name, waiter->lock_name)) {
52             return waiter;
53         }
54     }
55     return NULL;
56 }
57
58 /* Returns the waiter that owns 'lock'.
59  *
60  * A lock always has an owner, so this function will never return NULL. */
61 struct ovsdb_lock_waiter *
62 ovsdb_lock_get_owner(const struct ovsdb_lock *lock)
63 {
64     return CONTAINER_OF(list_front(&lock->waiters),
65                         struct ovsdb_lock_waiter, lock_node);
66 }
67
68 /* Removes 'waiter' from its lock's list.  This means that, if 'waiter' was
69  * formerly the owner of its lock, then it no longer owns it.
70  *
71  * Returns the session that now owns 'waiter'.  This is NULL if 'waiter' was
72  * the lock's owner and no other sessions were waiting for the lock.  In this
73  * case, the lock has been destroyed, so the caller must be sure not to refer
74  * to it again.  A nonnull return value reflects a change in the lock's
75  * ownership if and only if 'waiter' formerly owned the lock. */
76 struct ovsdb_session *
77 ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *waiter)
78 {
79     struct ovsdb_lock *lock = waiter->lock;
80
81     list_remove(&waiter->lock_node);
82     waiter->lock = NULL;
83
84     if (list_is_empty(&lock->waiters)) {
85         hmap_remove(&lock->server->locks, &lock->hmap_node);
86         free(lock->name);
87         free(lock);
88         return NULL;
89     }
90
91     return ovsdb_lock_get_owner(lock)->session;
92 }
93
94 /* Destroys 'waiter', which must have already been removed from its lock's
95  * waiting list with ovsdb_lock_waiter_remove().
96  *
97  * Removing and destroying locks are decoupled because a lock initially created
98  * by the "steal" request, that is later stolen by another client, remains in
99  * the database session until the database client sends an "unlock" request. */
100 void
101 ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *waiter)
102 {
103     assert(!waiter->lock);
104     hmap_remove(&waiter->session->waiters, &waiter->session_node);
105     free(waiter->lock_name);
106     free(waiter);
107 }
108
109 /* Returns true if 'waiter' owns its associated lock. */
110 bool
111 ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *waiter)
112 {
113     return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock);
114 }
115
116 /* Initializes 'server' as a server that operates on 'db'. */
117 void
118 ovsdb_server_init(struct ovsdb_server *server, struct ovsdb *db)
119 {
120     server->db = db;
121     hmap_init(&server->locks);
122 }
123
124 /* Destroys 'server'. */
125 void
126 ovsdb_server_destroy(struct ovsdb_server *server)
127 {
128     hmap_destroy(&server->locks);
129 }
130
131 static struct ovsdb_lock *
132 ovsdb_server_create_lock__(struct ovsdb_server *server, const char *lock_name,
133                            uint32_t hash)
134 {
135     struct ovsdb_lock *lock;
136
137     HMAP_FOR_EACH_WITH_HASH (lock, hmap_node, hash, &server->locks) {
138         if (!strcmp(lock->name, lock_name)) {
139             return lock;
140         }
141     }
142
143     lock = xzalloc(sizeof *lock);
144     lock->server = server;
145     lock->name = xstrdup(lock_name);
146     hmap_insert(&server->locks, &lock->hmap_node, hash);
147     list_init(&lock->waiters);
148
149     return lock;
150 }
151
152 /* Attempts to acquire the lock named 'lock_name' for 'session' within
153  * 'server'.  Returns the new lock waiter.
154  *
155  * If 'mode' is OVSDB_LOCK_STEAL, then the new lock waiter is always the owner
156  * of the lock.  '*victimp' receives the session of the previous owner or NULL
157  * if the lock was previously unowned.  (If the victim itself originally
158  * obtained the lock through a "steal" operation, then this function also
159  * removes the victim from the lock's waiting list.)
160  *
161  * If 'mode' is OVSDB_LOCK_WAIT, then the new lock waiter is the owner of the
162  * lock only if this lock had no existing owner.  '*victimp' is set to NULL. */
163 struct ovsdb_lock_waiter *
164 ovsdb_server_lock(struct ovsdb_server *server,
165                   struct ovsdb_session *session,
166                   const char *lock_name,
167                   enum ovsdb_lock_mode mode,
168                   struct ovsdb_session **victimp)
169 {
170     uint32_t hash = hash_string(lock_name, 0);
171     struct ovsdb_lock_waiter *waiter, *victim;
172     struct ovsdb_lock *lock;
173
174     lock = ovsdb_server_create_lock__(server, lock_name, hash);
175     victim = (mode == OVSDB_LOCK_STEAL && !list_is_empty(&lock->waiters)
176               ? ovsdb_lock_get_owner(lock)
177               : NULL);
178
179     waiter = xmalloc(sizeof *waiter);
180     waiter->mode = mode;
181     waiter->lock_name = xstrdup(lock_name);
182     waiter->lock = lock;
183     if (mode == OVSDB_LOCK_STEAL) {
184         list_push_front(&lock->waiters, &waiter->lock_node);
185     } else {
186         list_push_back(&lock->waiters, &waiter->lock_node);
187     }
188     waiter->session = session;
189     hmap_insert(&waiter->session->waiters, &waiter->session_node, hash);
190
191     if (victim && victim->mode == OVSDB_LOCK_STEAL) {
192         ovsdb_lock_waiter_remove(victim);
193     }
194     *victimp = victim ? victim->session : NULL;
195
196     return waiter;
197 }