BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file blocker.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Blocker module. Provides a statement that blocks when initialized, and which can be blocked
32 * and unblocked from outside.
33 *
34 * Synopsis: blocker([string initial_state])
35 * Description: provides blocking operations. On deinitialization, waits for all corresponding
36 * use() statements to die before dying itself.
37 * The optional boolean argument initial_state specifies the initial up-state
38 * of the blocker. If not given, the default is false (not up).
39 * Variables: string (empty) - the up-state (false or true).
40 *
41 * Synopsis: blocker::up()
42 * Description: sets the blocking state to up.
43 * The immediate effects of corresponding use() statements going up are processed before
44 * this statement goes up; but this statement statement still goes up immediately,
45 * assuming the effects mentioned haven't resulted in the intepreter scheduling this
46 * very statement for destruction.
47 *
48 * Synopsis: blocker::down()
49 * Description: sets the blocking state to down.
50 * The immediate effects of corresponding use() statements going up are processed before
51 * this statement goes up; but this statement statement still goes up immediately,
52 * assuming the effects mentioned haven't resulted in the intepreter scheduling this
53 * very statement for destruction.
54 *
55 * Synopsis: blocker::downup()
56 * Description: atomically sets the blocker to down state (if it was up), then (back) to up state.
57 * Note that this is not equivalent to calling down() and immediately up(); in that case,
58 * the interpreter will first handle the immediate effects of any use() statements
59 * going down as a result of having called down() and will only later execute the up()
60 * statement. In fact, it is possible that the effects of down() will prevent up() from
61 * executing, which may leave the program in an undesirable state.
62 *
63 * Synopsis: blocker::rdownup()
64 * Description: on deinitialization, atomically sets the blocker to down state (if it was up), then
65 * (back) to up state.
66 * The immediate effects of corresponding use() statements changing state are processed
67 * *after* the immediate effects of this statement dying (in contrast to downup()).
68 *
69 * Synopsis: blocker::use()
70 * Description: blocks on the blocker. This module is in up state if and only if the blocking state of
71 * the blocker is up. Multiple use statements may be used with the same blocker.
72 */
73  
74 #include <stdlib.h>
75 #include <string.h>
76  
77 #include <misc/offset.h>
78 #include <misc/debug.h>
79 #include <structure/LinkedList1.h>
80 #include <structure/LinkedList0.h>
81  
82 #include <ncd/module_common.h>
83  
84 #include <generated/blog_channel_ncd_blocker.h>
85  
86 struct instance {
87 NCDModuleInst *i;
88 LinkedList1 users;
89 LinkedList0 rdownups_list;
90 int up;
91 int dying;
92 };
93  
94 struct rdownup_instance {
95 NCDModuleInst *i;
96 struct instance *blocker;
97 LinkedList0Node rdownups_list_node;
98 };
99  
100 struct use_instance {
101 NCDModuleInst *i;
102 struct instance *blocker;
103 LinkedList1Node blocker_node;
104 };
105  
106 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
107 {
108 struct instance *o = vo;
109 o->i = i;
110  
111 // read arguments
112 NCDValRef initial_state = NCDVal_NewInvalid();
113 if (!NCDVal_ListRead(params->args, 0) &&
114 !NCDVal_ListRead(params->args, 1, &initial_state)
115 ) {
116 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
117 goto fail0;
118 }
119  
120 // get the initial state
121 o->up = 0;
122 if (!NCDVal_IsInvalid(initial_state)) {
123 if (!ncd_read_boolean(initial_state, &o->up)) {
124 ModuleLog(o->i, BLOG_ERROR, "bad initial_state argument");
125 goto fail0;
126 }
127 }
128  
129 // init users list
130 LinkedList1_Init(&o->users);
131  
132 // init rdownups list
133 LinkedList0_Init(&o->rdownups_list);
134  
135 // set not dying
136 o->dying = 0;
137  
138 // signal up
139 NCDModuleInst_Backend_Up(o->i);
140 return;
141  
142 fail0:
143 NCDModuleInst_Backend_DeadError(i);
144 }
145  
146 static void instance_free (struct instance *o)
147 {
148 ASSERT(LinkedList1_IsEmpty(&o->users))
149  
150 // break any rdownups
151 LinkedList0Node *ln;
152 while (ln = LinkedList0_GetFirst(&o->rdownups_list)) {
153 struct rdownup_instance *rdu = UPPER_OBJECT(ln, struct rdownup_instance, rdownups_list_node);
154 ASSERT(rdu->blocker == o)
155 LinkedList0_Remove(&o->rdownups_list, &rdu->rdownups_list_node);
156 rdu->blocker = NULL;
157 }
158  
159 NCDModuleInst_Backend_Dead(o->i);
160 }
161  
162 static void func_die (void *vo)
163 {
164 struct instance *o = vo;
165 ASSERT(!o->dying)
166  
167 // if we have no users, die right away, else wait for users
168 if (LinkedList1_IsEmpty(&o->users)) {
169 instance_free(o);
170 return;
171 }
172  
173 // set dying
174 o->dying = 1;
175 }
176  
177 static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
178 {
179 struct instance *o = vo;
180  
181 if (name == NCD_STRING_EMPTY) {
182 *out = ncd_make_boolean(mem, o->up);
183 return 1;
184 }
185  
186 return 0;
187 }
188  
189 static void updown_func_new_templ (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int up, int first_down)
190 {
191 ASSERT(!first_down || up)
192  
193 // check arguments
194 if (!NCDVal_ListRead(params->args, 0)) {
195 ModuleLog(i, BLOG_ERROR, "wrong arity");
196 goto fail0;
197 }
198  
199 // signal up
200 NCDModuleInst_Backend_Up(i);
201  
202 // get method object
203 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
204  
205 if (first_down || mo->up != up) {
206 // signal users
207 for (LinkedList1Node *node = LinkedList1_GetFirst(&mo->users); node; node = LinkedList1Node_Next(node)) {
208 struct use_instance *user = UPPER_OBJECT(node, struct use_instance, blocker_node);
209 ASSERT(user->blocker == mo)
210 if (first_down && mo->up) {
211 NCDModuleInst_Backend_Down(user->i);
212 }
213 if (up) {
214 NCDModuleInst_Backend_Up(user->i);
215 } else {
216 NCDModuleInst_Backend_Down(user->i);
217 }
218 }
219  
220 // change up state
221 mo->up = up;
222 }
223  
224 return;
225  
226 fail0:
227 NCDModuleInst_Backend_DeadError(i);
228 }
229  
230 static void up_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
231 {
232 updown_func_new_templ(i, params, 1, 0);
233 }
234  
235 static void down_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
236 {
237 updown_func_new_templ(i, params, 0, 0);
238 }
239  
240 static void downup_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
241 {
242 updown_func_new_templ(i, params, 1, 1);
243 }
244  
245 static void rdownup_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
246 {
247 struct rdownup_instance *o = vo;
248 o->i = i;
249  
250 // check arguments
251 if (!NCDVal_ListRead(params->args, 0)) {
252 ModuleLog(i, BLOG_ERROR, "wrong arity");
253 goto fail0;
254 }
255  
256 // get blocker
257 struct instance *blk = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
258  
259 // set blocker
260 o->blocker = blk;
261  
262 // insert to rdownups list
263 LinkedList0_Prepend(&blk->rdownups_list, &o->rdownups_list_node);
264  
265 // signal up
266 NCDModuleInst_Backend_Up(i);
267 return;
268  
269 fail0:
270 NCDModuleInst_Backend_DeadError(i);
271 }
272  
273 static void rdownup_func_die (void *vo)
274 {
275 struct rdownup_instance *o = vo;
276  
277 struct instance *blk = o->blocker;
278  
279 if (blk) {
280 // remove from rdownups list
281 LinkedList0_Remove(&blk->rdownups_list, &o->rdownups_list_node);
282  
283 // downup users
284 for (LinkedList1Node *ln = LinkedList1_GetFirst(&blk->users); ln; ln = LinkedList1Node_Next(ln)) {
285 struct use_instance *user = UPPER_OBJECT(ln, struct use_instance, blocker_node);
286 ASSERT(user->blocker == blk)
287 if (blk->up) {
288 NCDModuleInst_Backend_Down(user->i);
289 }
290 NCDModuleInst_Backend_Up(user->i);
291 }
292  
293 // set up
294 blk->up = 1;
295 }
296  
297 NCDModuleInst_Backend_Dead(o->i);
298 }
299  
300 static void use_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
301 {
302 struct use_instance *o = vo;
303 o->i = i;
304  
305 // check arguments
306 if (!NCDVal_ListRead(params->args, 0)) {
307 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
308 goto fail0;
309 }
310  
311 // set blocker
312 o->blocker = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
313  
314 // add to blocker's list
315 LinkedList1_Append(&o->blocker->users, &o->blocker_node);
316  
317 // signal up if needed
318 if (o->blocker->up) {
319 NCDModuleInst_Backend_Up(o->i);
320 }
321  
322 return;
323  
324 fail0:
325 NCDModuleInst_Backend_DeadError(i);
326 }
327  
328 static void use_func_die (void *vo)
329 {
330 struct use_instance *o = vo;
331  
332 // remove from blocker's list
333 LinkedList1_Remove(&o->blocker->users, &o->blocker_node);
334  
335 // make the blocker die if needed
336 if (o->blocker->dying && LinkedList1_IsEmpty(&o->blocker->users)) {
337 instance_free(o->blocker);
338 }
339  
340 NCDModuleInst_Backend_Dead(o->i);
341 }
342  
343 static struct NCDModule modules[] = {
344 {
345 .type = "blocker",
346 .func_new2 = func_new,
347 .func_die = func_die,
348 .func_getvar2 = func_getvar2,
349 .alloc_size = sizeof(struct instance)
350 }, {
351 .type = "blocker::up",
352 .func_new2 = up_func_new
353 }, {
354 .type = "blocker::down",
355 .func_new2 = down_func_new
356 }, {
357 .type = "blocker::downup",
358 .func_new2 = downup_func_new
359 }, {
360 .type = "blocker::rdownup",
361 .func_new2 = rdownup_func_new,
362 .func_die = rdownup_func_die,
363 .alloc_size = sizeof(struct rdownup_instance)
364 }, {
365 .type = "blocker::use",
366 .func_new2 = use_func_new,
367 .func_die = use_func_die,
368 .alloc_size = sizeof(struct use_instance)
369 }, {
370 .type = NULL
371 }
372 };
373  
374 const struct NCDModuleGroup ncdmodule_blocker = {
375 .modules = modules
376 };