BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file try.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 *
32 * Synopsis:
33 * try(string template_name, list args)
34 * do(string template_name[, string interrupt_template_name])
35 *
36 * Description:
37 * The basic functionality is:
38 * 1. Starts a template process from the specified template and arguments.
39 * 2. Waits for the process to initialize completely, or for a _try->assert()
40 * assertion to fail or a _do->break() call.
41 * 3. Initiates termination of the process and waits for it to terminate.
42 * 4. Goes to up state. The "succeeded" variable reflects whether the process
43 * managed to initialize, or an assertion failed.
44 * If at any point during these steps termination of the try statement is
45 * requested, requests the process to terminate (if not already), and dies
46 * when it terminates.
47 *
48 * The differences between try() and do() are that do() directly exposes
49 * the caller scope (try() does via _caller), and the availability of
50 * assert/break.
51 *
52 * The two-argument version of do() is an extension, where in case of a
53 * statement termination request while the template process is not yet
54 * initialized completely, the interrupt_template_name template process is
55 * started, instead of sending a termination request to the template_name
56 * process. The interrupt_template_name process has access to the same
57 * scope as template_name. This means that it can itself call _do->break().
58 * Termination of the interrupt_template_name process is started as soon
59 * as it initializes completely, or when termination of the template_name
60 * process starts.
61 *
62 * Variables:
63 * string succeeded - "true" if the template process finished, "false" if assert
64 * or break was called.
65 *
66 *
67 * Synopsis:
68 * try.try::assert(string cond)
69 *
70 * Description:
71 * Call as _try->assert() from the template process of try(). If cond is
72 * "true", does nothing. Else, initiates termination of the process (if not
73 * already), and marks the try operation as not succeeded.
74 *
75 *
76 * Synopsis:
77 * do.do::break()
78 * do.do::rbreak()
79 *
80 * Description:
81 * Call as _do->break() from the template process of do() to initiate
82 * premature termination, marking the do operation as not succeeded.
83 * The rbreak() does so on deinitialization.
84 */
85  
86 #include <stdlib.h>
87 #include <string.h>
88  
89 #include <misc/offset.h>
90  
91 #include <ncd/module_common.h>
92  
93 #include <generated/blog_channel_ncd_try.h>
94  
95 struct instance {
96 NCDModuleInst *i;
97 int is_do;
98 NCDValRef interrupt_template_name;
99 NCDModuleProcess process;
100 NCDModuleProcess interrupt_process;
101 int state;
102 int intstate;
103 int dying;
104 int succeeded;
105 };
106  
107 struct rbreak_instance {
108 NCDModuleInst *i;
109 NCDModuleRef ref;
110 };
111  
112 enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED, STATE_WAITINT};
113 enum {INTSTATE_NONE, INTSTATE_INIT, INTSTATE_DEINIT};
114  
115 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
116 static void start_terminating (struct instance *o);
117 static void instance_free (struct instance *o);
118  
119 enum {STRING_TRY, STRING_TRY_TRY, STRING_DO, STRING_DO_DO};
120  
121 static const char *strings[] = {"_try", "try.try", "_do", "do.do"};
122  
123 static void process_handler_event (NCDModuleProcess *process, int event)
124 {
125 struct instance *o = UPPER_OBJECT(process, struct instance, process);
126  
127 switch (event) {
128 case NCDMODULEPROCESS_EVENT_UP: {
129 ASSERT(o->state == STATE_INIT)
130  
131 // start terminating
132 start_terminating(o);
133 } break;
134  
135 case NCDMODULEPROCESS_EVENT_DOWN: {
136 // Can't happen since we start terminating with it comes up.
137 ASSERT(0)
138 } break;
139  
140 case NCDMODULEPROCESS_EVENT_TERMINATED: {
141 ASSERT(o->state == STATE_DEINIT)
142  
143 // free process
144 NCDModuleProcess_Free(&o->process);
145  
146 if (o->dying) {
147 // We want to die but not without interrupt_process still running.
148 if (o->intstate == INTSTATE_NONE) {
149 instance_free(o);
150 } else {
151 o->state = STATE_WAITINT;
152 }
153 return;
154 }
155  
156 // signal up
157 NCDModuleInst_Backend_Up(o->i);
158  
159 // set state finished
160 o->state = STATE_FINISHED;
161 } break;
162 }
163 }
164  
165 static void interrupt_process_handler_event (NCDModuleProcess *process, int event)
166 {
167 struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
168 ASSERT(o->dying)
169 ASSERT(o->intstate != INTSTATE_NONE)
170  
171 switch (event) {
172 case NCDMODULEPROCESS_EVENT_UP: {
173 ASSERT(o->intstate == INTSTATE_INIT)
174  
175 // Start terminating the interrupt_process.
176 NCDModuleProcess_Terminate(&o->interrupt_process);
177 o->intstate = INTSTATE_DEINIT;
178 } break;
179  
180 case NCDMODULEPROCESS_EVENT_DOWN: {
181 // Can't happen since we start terminating with it comes up.
182 ASSERT(0)
183 } break;
184  
185 case NCDMODULEPROCESS_EVENT_TERMINATED: {
186 ASSERT(o->intstate == INTSTATE_DEINIT)
187  
188 // free process
189 NCDModuleProcess_Free(&o->interrupt_process);
190 o->intstate = INTSTATE_NONE;
191  
192 // If the main process has terminated, free the instance now.
193 if (o->state == STATE_WAITINT) {
194 instance_free(o);
195 return;
196 }
197 } break;
198 }
199 }
200  
201 static int common_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
202 {
203 if (o->is_do) {
204 if (name == ModuleString(o->i, STRING_DO)) {
205 *out_object = NCDObject_Build(ModuleString(o->i, STRING_DO_DO), o, NCDObject_no_getvar, NCDObject_no_getobj);
206 return 1;
207 }
208  
209 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
210 } else {
211 if (name == NCD_STRING_CALLER) {
212 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj);
213 return 1;
214 }
215  
216 if (name == ModuleString(o->i, STRING_TRY)) {
217 *out_object = NCDObject_Build(ModuleString(o->i, STRING_TRY_TRY), o, NCDObject_no_getvar, NCDObject_no_getobj);
218 return 1;
219 }
220  
221 return 0;
222 }
223 }
224  
225 static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
226 {
227 struct instance *o = UPPER_OBJECT(process, struct instance, process);
228 return common_getspecialobj(o, name, out_object);
229 }
230  
231 static int interrupt_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
232 {
233 struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
234 return common_getspecialobj(o, name, out_object);
235 }
236  
237 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
238 {
239 struct instance *o = NCDObject_DataPtr(obj);
240  
241 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
242 }
243  
244 static void start_terminating (struct instance *o)
245 {
246 ASSERT(o->state == STATE_INIT)
247  
248 // request termination of the interrupt_process if possible
249 if (o->intstate == INTSTATE_INIT) {
250 NCDModuleProcess_Terminate(&o->interrupt_process);
251 o->intstate = INTSTATE_DEINIT;
252 }
253  
254 // request process termination
255 NCDModuleProcess_Terminate(&o->process);
256 o->state = STATE_DEINIT;
257 }
258  
259 static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args, NCDValRef interrupt_template_name)
260 {
261 struct instance *o = vo;
262 o->i = i;
263 o->is_do = is_do;
264 o->interrupt_template_name = interrupt_template_name;
265  
266 // start process
267 if (!NCDModuleProcess_InitValue(&o->process, i, template_name, args, process_handler_event)) {
268 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
269 goto fail0;
270 }
271  
272 // set special object function
273 NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj);
274  
275 // set state init, not dying, assume succeeded
276 o->state = STATE_INIT;
277 o->intstate = INTSTATE_NONE;
278 o->dying = 0;
279 o->succeeded = 1;
280 return;
281  
282 fail0:
283 NCDModuleInst_Backend_DeadError(i);
284 }
285  
286 static void func_new_try (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
287 {
288 // check arguments
289 NCDValRef template_name_arg;
290 NCDValRef args_arg;
291 if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) {
292 ModuleLog(i, BLOG_ERROR, "wrong arity");
293 goto fail;
294 }
295 if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) {
296 ModuleLog(i, BLOG_ERROR, "wrong type");
297 goto fail;
298 }
299  
300 return func_new_common(vo, i, params, 0, template_name_arg, args_arg, NCDVal_NewInvalid());
301  
302 fail:
303 NCDModuleInst_Backend_DeadError(i);
304 }
305  
306 static void func_new_do (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
307 {
308 // check arguments
309 NCDValRef template_name_arg;
310 NCDValRef interrupt_template_name_arg = NCDVal_NewInvalid();
311 if (!NCDVal_ListRead(params->args, 1, &template_name_arg) &&
312 !NCDVal_ListRead(params->args, 2, &template_name_arg, &interrupt_template_name_arg)
313 ) {
314 ModuleLog(i, BLOG_ERROR, "wrong arity");
315 goto fail;
316 }
317 if (!NCDVal_IsString(template_name_arg) ||
318 (!NCDVal_IsInvalid(interrupt_template_name_arg) && !NCDVal_IsString(interrupt_template_name_arg))
319 ) {
320 ModuleLog(i, BLOG_ERROR, "wrong type");
321 goto fail;
322 }
323  
324 return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid(), interrupt_template_name_arg);
325  
326 fail:
327 NCDModuleInst_Backend_DeadError(i);
328 }
329  
330 static void instance_free (struct instance *o)
331 {
332 ASSERT(o->intstate == INTSTATE_NONE)
333  
334 NCDModuleInst_Backend_Dead(o->i);
335 }
336  
337 static void instance_break (struct instance *o)
338 {
339 // mark not succeeded
340 o->succeeded = 0;
341  
342 // start terminating if not already
343 if (o->state == STATE_INIT) {
344 start_terminating(o);
345 }
346 }
347  
348 static void func_die (void *vo)
349 {
350 struct instance *o = vo;
351 ASSERT(!o->dying)
352 ASSERT(o->intstate == INTSTATE_NONE)
353  
354 // if we're finished, die immediately
355 if (o->state == STATE_FINISHED) {
356 instance_free(o);
357 return;
358 }
359  
360 // set dying
361 o->dying = 1;
362  
363 // if already terminating, nothing to do
364 if (o->state != STATE_INIT) {
365 return;
366 }
367  
368 // if we don't have the interrupt-template, start terminating
369 if (NCDVal_IsInvalid(o->interrupt_template_name)) {
370 goto terminate;
371 }
372  
373 // start process
374 if (!NCDModuleProcess_InitValue(&o->interrupt_process, o->i, o->interrupt_template_name, NCDVal_NewInvalid(), interrupt_process_handler_event)) {
375 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
376 goto terminate;
377 }
378 NCDModuleProcess_SetSpecialFuncs(&o->interrupt_process, interrupt_process_func_getspecialobj);
379 o->intstate = INTSTATE_INIT;
380 return;
381  
382 terminate:
383 start_terminating(o);
384 }
385  
386 static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
387 {
388 struct instance *o = vo;
389 ASSERT(o->state == STATE_FINISHED)
390 ASSERT(!o->dying)
391  
392 if (name == NCD_STRING_SUCCEEDED) {
393 *out = ncd_make_boolean(mem, o->succeeded);
394 return 1;
395 }
396  
397 return 0;
398 }
399  
400 static void assert_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
401 {
402 // check arguments
403 NCDValRef cond_arg;
404 if (!NCDVal_ListRead(params->args, 1, &cond_arg)) {
405 ModuleLog(i, BLOG_ERROR, "wrong arity");
406 goto fail1;
407 }
408 if (!NCDVal_IsString(cond_arg)) {
409 ModuleLog(i, BLOG_ERROR, "wrong type");
410 goto fail1;
411 }
412  
413 // signal up
414 NCDModuleInst_Backend_Up(i);
415  
416 // break if needed
417 if (!NCDVal_StringEquals(cond_arg, "true")) {
418 instance_break((struct instance *)params->method_user);
419 }
420  
421 return;
422  
423 fail1:
424 NCDModuleInst_Backend_DeadError(i);
425 }
426  
427 static void break_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
428 {
429 // check arguments
430 if (!NCDVal_ListRead(params->args, 0)) {
431 ModuleLog(i, BLOG_ERROR, "wrong arity");
432 goto fail1;
433 }
434  
435 // signal up
436 NCDModuleInst_Backend_Up(i);
437  
438 // break
439 instance_break((struct instance *)params->method_user);
440  
441 return;
442  
443 fail1:
444 NCDModuleInst_Backend_DeadError(i);
445 }
446  
447 static void rbreak_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
448 {
449 struct rbreak_instance *o = vo;
450 o->i = i;
451  
452 // check arguments
453 if (!NCDVal_ListRead(params->args, 0)) {
454 ModuleLog(i, BLOG_ERROR, "wrong arity");
455 goto fail0;
456 }
457  
458 // init object reference
459 NCDModuleRef_Init(&o->ref, ((struct instance *)params->method_user)->i);
460  
461 // go up
462 NCDModuleInst_Backend_Up(i);
463 return;
464  
465 fail0:
466 NCDModuleInst_Backend_DeadError(i);
467 }
468  
469 static void rbreak_func_die (void *vo)
470 {
471 struct rbreak_instance *o = vo;
472  
473 // deref backtrack_point
474 NCDModuleInst *inst = NCDModuleRef_Deref(&o->ref);
475  
476 // free object reference
477 NCDModuleRef_Free(&o->ref);
478  
479 // die
480 NCDModuleInst_Backend_Dead(o->i);
481  
482 // break
483 if (inst) {
484 instance_break((struct instance *)NCDModuleInst_Backend_GetUser(inst));
485 }
486 }
487  
488 static struct NCDModule modules[] = {
489 {
490 .type = "try",
491 .func_new2 = func_new_try,
492 .func_die = func_die,
493 .func_getvar2 = func_getvar2,
494 .alloc_size = sizeof(struct instance)
495 }, {
496 .type = "do",
497 .func_new2 = func_new_do,
498 .func_die = func_die,
499 .func_getvar2 = func_getvar2,
500 .alloc_size = sizeof(struct instance)
501 }, {
502 .type = "try.try::assert",
503 .func_new2 = assert_func_new
504 }, {
505 .type = "do.do::break",
506 .func_new2 = break_func_new
507 }, {
508 .type = "do.do::rbreak",
509 .func_new2 = rbreak_func_new,
510 .func_die = rbreak_func_die,
511 .alloc_size = sizeof(struct rbreak_instance)
512 }, {
513 .type = NULL
514 }
515 };
516  
517 const struct NCDModuleGroup ncdmodule_try = {
518 .modules = modules,
519 .strings = strings
520 };