BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file NCDInterpreter.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  
30 #include <stdint.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <stdarg.h>
35  
36 #include <misc/offset.h>
37 #include <misc/balloc.h>
38 #include <misc/expstring.h>
39 #include <base/BLog.h>
40 #include <ncd/NCDSugar.h>
41 #include <ncd/modules/modules.h>
42  
43 #include "NCDInterpreter.h"
44  
45 #include <generated/blog_channel_ncd.h>
46  
47 #define SSTATE_CHILD 0
48 #define SSTATE_ADULT 1
49 #define SSTATE_DYING 2
50 #define SSTATE_FORGOTTEN 3
51  
52 #define PSTATE_WORKING 0
53 #define PSTATE_UP 1
54 #define PSTATE_WAITING 2
55 #define PSTATE_TERMINATING 3
56  
57 struct statement {
58 NCDModuleInst inst;
59 NCDValMem args_mem;
60 int mem_size;
61 int i;
62 };
63  
64 struct process {
65 NCDInterpreter *interp;
66 BReactor *reactor;
67 NCDInterpProcess *iprocess;
68 NCDModuleProcess *module_process;
69 BSmallTimer wait_timer;
70 BSmallPending work_job;
71 LinkedList1Node list_node; // node in processes
72 int ap;
73 int fp;
74 int num_statements;
75 unsigned int error:1;
76 unsigned int have_alloc:1;
77 #ifndef NDEBUG
78 int state;
79 #endif
80 struct statement statements[];
81 };
82  
83 struct func_call_context {
84 struct statement *ps;
85 NCDEvaluatorArgs *args;
86 };
87  
88 static void start_terminate (NCDInterpreter *interp, int exit_code);
89 static char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del);
90 static void clear_process_cache (NCDInterpreter *interp);
91 static struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess);
92 static void process_release (struct process *p, int no_push);
93 static void process_assert_statements_cleared (struct process *p);
94 static int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process);
95 static void process_free (struct process *p, NCDModuleProcess **out_mp);
96 static void process_set_state (struct process *p, int state);
97 static void process_start_terminating (struct process *p);
98 static int process_have_child (struct process *p);
99 static void process_assert_pointers (struct process *p);
100 static void process_logfunc (struct process *p);
101 static void process_log (struct process *p, int level, const char *fmt, ...);
102 static void process_work_job_handler_working (struct process *p);
103 static void process_work_job_handler_up (struct process *p);
104 static void process_work_job_handler_waiting (struct process *p);
105 static void process_work_job_handler_terminating (struct process *p);
106 static int eval_func_eval_var (void *user, NCD_string_id_t const *varnames, size_t num_names, NCDValMem *mem, NCDValRef *out);
107 static int eval_func_eval_call (void *user, NCD_string_id_t func_name_id, NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out);
108 static void process_advance (struct process *p);
109 static void process_wait_timer_handler (BSmallTimer *timer);
110 static int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object);
111 static int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object);
112 static int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value);
113 static void statement_logfunc (struct statement *ps);
114 static void statement_log (struct statement *ps, int level, const char *fmt, ...);
115 static struct process * statement_process (struct statement *ps);
116 static int statement_mem_is_allocated (struct statement *ps);
117 static int statement_mem_size (struct statement *ps);
118 static int statement_allocate_memory (struct statement *ps, int alloc_size);
119 static void statement_instance_func_event (NCDModuleInst *inst, int event);
120 static int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object);
121 static int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess *mp, NCD_string_id_t template_name);
122 static void statement_instance_logfunc (NCDModuleInst *inst);
123 static void statement_instance_func_interp_exit (void *vinterp, int exit_code);
124 static int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value);
125 static btime_t statement_instance_func_interp_getretrytime (void *vinterp);
126 static int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group);
127 static void process_moduleprocess_func_event (struct process *p, int event);
128 static int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object);
129 static void function_logfunc (void *user);
130 static int function_eval_arg (void *user, size_t index, NCDValMem *mem, NCDValRef *out);
131  
132 #define STATEMENT_LOG(ps, channel, ...) if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, channel)) statement_log(ps, channel, __VA_ARGS__)
133  
134 int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterpreter_params params)
135 {
136 ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE));
137 ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE_GUARD));
138 ASSERT(params.handler_finished);
139 ASSERT(params.num_extra_args >= 0);
140 ASSERT(params.reactor);
141 #ifndef BADVPN_NO_PROCESS
142 ASSERT(params.manager);
143 #endif
144 #ifndef BADVPN_NO_UDEV
145 ASSERT(params.umanager);
146 #endif
147 #ifndef BADVPN_NO_RANDOM
148 ASSERT(params.random2);
149 #endif
150  
151 // set params
152 o->params = params;
153  
154 // set not terminating
155 o->terminating = 0;
156  
157 // set program
158 o->program = program;
159  
160 // init string index
161 if (!NCDStringIndex_Init(&o->string_index)) {
162 BLog(BLOG_ERROR, "NCDStringIndex_Init failed");
163 goto fail0;
164 }
165  
166 // init module index
167 if (!NCDModuleIndex_Init(&o->mindex, &o->string_index)) {
168 BLog(BLOG_ERROR, "NCDModuleIndex_Init failed");
169 goto fail2;
170 }
171  
172 // init pointers to global resources in out struct NCDModuleInst_iparams.
173 // Don't initialize any callback at this point as these must not be called
174 // from globalinit functions of modules.
175 o->module_iparams.reactor = params.reactor;
176 #ifndef BADVPN_NO_PROCESS
177 o->module_iparams.manager = params.manager;
178 #endif
179 #ifndef BADVPN_NO_UDEV
180 o->module_iparams.umanager = params.umanager;
181 #endif
182 #ifndef BADVPN_NO_RANDOM
183 o->module_iparams.random2 = params.random2;
184 #endif
185 o->module_iparams.string_index = &o->string_index;
186  
187 // add module groups to index and allocate string id's for base_type's
188 for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) {
189 if (!NCDModuleIndex_AddGroup(&o->mindex, *g, &o->module_iparams, &o->string_index)) {
190 BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed");
191 goto fail3;
192 }
193 }
194  
195 // desugar
196 if (!NCDSugar_Desugar(&o->program)) {
197 BLog(BLOG_ERROR, "NCDSugar_Desugar failed");
198 goto fail3;
199 }
200  
201 // init expression evaluator
202 if (!NCDEvaluator_Init(&o->evaluator, &o->string_index)) {
203 BLog(BLOG_ERROR, "NCDEvaluator_Init failed");
204 goto fail3;
205 }
206  
207 // init interp program
208 if (!NCDInterpProg_Init(&o->iprogram, &o->program, &o->string_index, &o->evaluator, &o->mindex)) {
209 BLog(BLOG_ERROR, "NCDInterpProg_Init failed");
210 goto fail5;
211 }
212  
213 // init the rest of the module parameters structures
214 o->module_params.func_event = statement_instance_func_event;
215 o->module_params.func_getobj = statement_instance_func_getobj;
216 o->module_params.logfunc = (BLog_logfunc)statement_instance_logfunc;
217 o->module_params.iparams = &o->module_iparams;
218 o->module_iparams.user = o;
219 o->module_iparams.func_initprocess = statement_instance_func_initprocess;
220 o->module_iparams.func_interp_exit = statement_instance_func_interp_exit;
221 o->module_iparams.func_interp_getargs = statement_instance_func_interp_getargs;
222 o->module_iparams.func_interp_getretrytime = statement_instance_func_interp_getretrytime;
223 o->module_iparams.func_loadgroup = statement_instance_func_interp_loadgroup;
224 o->module_call_shared.logfunc = function_logfunc;
225 o->module_call_shared.func_eval_arg = function_eval_arg;
226 o->module_call_shared.iparams = &o->module_iparams;
227  
228 // init processes list
229 LinkedList1_Init(&o->processes);
230  
231 // init processes
232 for (NCDProgramElem *elem = NCDProgram_FirstElem(&o->program); elem; elem = NCDProgram_NextElem(&o->program, elem)) {
233 ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS)
234 NCDProcess *p = NCDProgramElem_Process(elem);
235  
236 if (NCDProcess_IsTemplate(p)) {
237 continue;
238 }
239  
240 // get string id for process name
241 NCD_string_id_t name_id = NCDStringIndex_Lookup(&o->string_index, NCDProcess_Name(p));
242 ASSERT(name_id >= 0)
243  
244 // find iprocess
245 NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&o->iprogram, name_id);
246 ASSERT(iprocess)
247  
248 if (!process_new(o, iprocess, NULL)) {
249 BLog(BLOG_ERROR, "failed to initialize process, exiting");
250 goto fail7;
251 }
252 }
253  
254 DebugObject_Init(&o->d_obj);
255 return 1;
256  
257 fail7:;
258 // free processes
259 LinkedList1Node *ln;
260 while (ln = LinkedList1_GetFirst(&o->processes)) {
261 struct process *p = UPPER_OBJECT(ln, struct process, list_node);
262 BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor));
263 NCDModuleProcess *mp;
264 process_free(p, &mp);
265 ASSERT(!mp)
266 }
267 // clear process cache (process_free() above may push to cache)
268 clear_process_cache(o);
269 // free interp program
270 NCDInterpProg_Free(&o->iprogram);
271 fail5:
272 // free evaluator
273 NCDEvaluator_Free(&o->evaluator);
274 fail3:
275 // free module index
276 NCDModuleIndex_Free(&o->mindex);
277 fail2:
278 // free string index
279 NCDStringIndex_Free(&o->string_index);
280 fail0:
281 // free program AST
282 NCDProgram_Free(&o->program);
283 return 0;
284 }
285  
286 void NCDInterpreter_Free (NCDInterpreter *o)
287 {
288 DebugObject_Free(&o->d_obj);
289 // any process that exists must be completely uninitialized
290  
291 // free processes
292 LinkedList1Node *ln;
293 while (ln = LinkedList1_GetFirst(&o->processes)) {
294 struct process *p = UPPER_OBJECT(ln, struct process, list_node);
295 BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor));
296 NCDModuleProcess *mp;
297 process_free(p, &mp);
298 ASSERT(!mp)
299 }
300  
301 // clear process cache
302 clear_process_cache(o);
303  
304 // free interp program
305 NCDInterpProg_Free(&o->iprogram);
306  
307 // free evaluator
308 NCDEvaluator_Free(&o->evaluator);
309  
310 // free module index
311 NCDModuleIndex_Free(&o->mindex);
312  
313 // free string index
314 NCDStringIndex_Free(&o->string_index);
315  
316 // free program AST
317 NCDProgram_Free(&o->program);
318 }
319  
320 void NCDInterpreter_RequestShutdown (NCDInterpreter *o, int exit_code)
321 {
322 DebugObject_Access(&o->d_obj);
323  
324 start_terminate(o, exit_code);
325 }
326  
327 void start_terminate (NCDInterpreter *interp, int exit_code)
328 {
329 // remember exit code
330 interp->main_exit_code = exit_code;
331  
332 // if we're already terminating, there's nothing to do
333 if (interp->terminating) {
334 return;
335 }
336  
337 // set the terminating flag
338 interp->terminating = 1;
339  
340 // if there are no processes, we're done
341 if (LinkedList1_IsEmpty(&interp->processes)) {
342 interp->params.handler_finished(interp->params.user, interp->main_exit_code);
343 return;
344 }
345  
346 // start terminating non-template processes
347 for (LinkedList1Node *ln = LinkedList1_GetFirst(&interp->processes); ln; ln = LinkedList1Node_Next(ln)) {
348 struct process *p = UPPER_OBJECT(ln, struct process, list_node);
349 if (p->module_process) {
350 continue;
351 }
352 process_start_terminating(p);
353 }
354 }
355  
356 char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del)
357 {
358 ExpString str;
359 if (!ExpString_Init(&str)) {
360 goto fail0;
361 }
362  
363 int is_first = 1;
364  
365 while (num_names > 0) {
366 if (!is_first && !ExpString_AppendChar(&str, del)) {
367 goto fail1;
368 }
369 const char *name_str = NCDStringIndex_Value(&interp->string_index, *names).ptr;
370 if (!ExpString_Append(&str, name_str)) {
371 goto fail1;
372 }
373 names++;
374 num_names--;
375 is_first = 0;
376 }
377  
378 return ExpString_Get(&str);
379  
380 fail1:
381 ExpString_Free(&str);
382 fail0:
383 return NULL;
384 }
385  
386 void clear_process_cache (NCDInterpreter *interp)
387 {
388 for (NCDProgramElem *elem = NCDProgram_FirstElem(&interp->program); elem; elem = NCDProgram_NextElem(&interp->program, elem)) {
389 ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS)
390 NCDProcess *ast_p = NCDProgramElem_Process(elem);
391  
392 NCD_string_id_t name_id = NCDStringIndex_Lookup(&interp->string_index, NCDProcess_Name(ast_p));
393 NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, name_id);
394  
395 struct process *p;
396 while (p = NCDInterpProcess_CachePull(iprocess)) {
397 process_release(p, 1);
398 }
399 }
400 }
401  
402 struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess)
403 {
404 ASSERT(iprocess)
405  
406 // try to pull from cache
407 struct process *p = NCDInterpProcess_CachePull(iprocess);
408 if (p) {
409 goto allocated;
410 }
411  
412 // get number of statements
413 int num_statements = NCDInterpProcess_NumStatements(iprocess);
414  
415 // get size of preallocated memory
416 int mem_size = NCDInterpProcess_PreallocSize(iprocess);
417 if (mem_size < 0) {
418 goto fail0;
419 }
420  
421 // start with size of process structure
422 size_t alloc_size = sizeof(struct process);
423  
424 // add size of statements array
425 if (num_statements > SIZE_MAX / sizeof(struct statement)) {
426 goto fail0;
427 }
428 if (!BSizeAdd(&alloc_size, num_statements * sizeof(struct statement))) {
429 goto fail0;
430 }
431  
432 // align for preallocated memory
433 if (!BSizeAlign(&alloc_size, BMAX_ALIGN)) {
434 goto fail0;
435 }
436 size_t mem_off = alloc_size;
437  
438 // add size of preallocated memory
439 if (mem_size > SIZE_MAX || !BSizeAdd(&alloc_size, mem_size)) {
440 goto fail0;
441 }
442  
443 // allocate memory
444 p = BAlloc(alloc_size);
445 if (!p) {
446 goto fail0;
447 }
448  
449 // set variables
450 p->interp = interp;
451 p->reactor = interp->params.reactor;
452 p->iprocess = iprocess;
453 p->ap = 0;
454 p->fp = 0;
455 p->num_statements = num_statements;
456 p->error = 0;
457 p->have_alloc = 0;
458  
459 // init statements
460 char *mem = (char *)p + mem_off;
461 for (int i = 0; i < num_statements; i++) {
462 struct statement *ps = &p->statements[i];
463 ps->i = i;
464 ps->inst.istate = SSTATE_FORGOTTEN;
465 ps->mem_size = NCDInterpProcess_StatementPreallocSize(iprocess, i);
466 ps->inst.mem = mem + NCDInterpProcess_StatementPreallocOffset(iprocess, i);
467 }
468  
469 // init timer
470 BSmallTimer_Init(&p->wait_timer, process_wait_timer_handler);
471  
472 // init work job
473 BSmallPending_Init(&p->work_job, BReactor_PendingGroup(p->reactor), NULL, NULL);
474  
475 allocated:
476 ASSERT(p->interp == interp)
477 ASSERT(p->reactor == interp->params.reactor)
478 ASSERT(p->iprocess == iprocess)
479 ASSERT(p->ap == 0)
480 ASSERT(p->fp == 0)
481 ASSERT(p->num_statements == NCDInterpProcess_NumStatements(iprocess))
482 ASSERT(p->error == 0)
483 process_assert_statements_cleared(p);
484 ASSERT(!BSmallPending_IsSet(&p->work_job))
485 ASSERT(!BSmallTimer_IsRunning(&p->wait_timer))
486  
487 return p;
488  
489 fail0:
490 BLog(BLOG_ERROR, "failed to allocate memory for process %s", NCDInterpProcess_Name(iprocess));
491 return NULL;
492 }
493  
494 void process_release (struct process *p, int no_push)
495 {
496 ASSERT(p->ap == 0)
497 ASSERT(p->fp == 0)
498 ASSERT(p->error == 0)
499 process_assert_statements_cleared(p);
500 ASSERT(!BSmallPending_IsSet(&p->work_job))
501 ASSERT(!BSmallTimer_IsRunning(&p->wait_timer))
502  
503 // try to push to cache
504 if (!no_push && !p->have_alloc) {
505 if (NCDInterpProcess_CachePush(p->iprocess, p)) {
506 return;
507 }
508 }
509  
510 // free work job
511 BSmallPending_Free(&p->work_job, BReactor_PendingGroup(p->reactor));
512  
513 // free statement memory
514 if (p->have_alloc) {
515 for (int i = 0; i < p->num_statements; i++) {
516 struct statement *ps = &p->statements[i];
517 if (statement_mem_is_allocated(ps)) {
518 free(ps->inst.mem);
519 }
520 }
521 }
522  
523 // free strucure
524 BFree(p);
525 }
526  
527 void process_assert_statements_cleared (struct process *p)
528 {
529 #ifndef NDEBUG
530 for (int i = 0; i < p->num_statements; i++) {
531 ASSERT(p->statements[i].i == i)
532 ASSERT(p->statements[i].inst.istate == SSTATE_FORGOTTEN)
533 }
534 #endif
535 }
536  
537 int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process)
538 {
539 ASSERT(iprocess)
540  
541 // allocate prepared process struct
542 struct process *p = process_allocate(interp, iprocess);
543 if (!p) {
544 return 0;
545 }
546  
547 // set module process pointer
548 p->module_process = module_process;
549  
550 // set module process handlers
551 if (p->module_process) {
552 NCDModuleProcess_Interp_SetHandlers(p->module_process, p,
553 (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event,
554 (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj);
555 }
556  
557 // set state
558 process_set_state(p, PSTATE_WORKING);
559 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p);
560  
561 // insert to processes list
562 LinkedList1_Append(&interp->processes, &p->list_node);
563  
564 // schedule work
565 BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
566  
567 return 1;
568 }
569  
570 void process_set_state (struct process *p, int state)
571 {
572 #ifndef NDEBUG
573 p->state = state;
574 #endif
575 }
576  
577 void process_free (struct process *p, NCDModuleProcess **out_mp)
578 {
579 ASSERT(p->ap == 0)
580 ASSERT(p->fp == 0)
581 ASSERT(out_mp)
582 ASSERT(!BSmallPending_IsSet(&p->work_job))
583  
584 // give module process to caller so it can inform the process creator that the process has terminated
585 *out_mp = p->module_process;
586  
587 // remove from processes list
588 LinkedList1_Remove(&p->interp->processes, &p->list_node);
589  
590 // free timer
591 BReactor_RemoveSmallTimer(p->reactor, &p->wait_timer);
592  
593 // clear error
594 p->error = 0;
595  
596 process_release(p, 0);
597 }
598  
599 void process_start_terminating (struct process *p)
600 {
601 // set state terminating
602 process_set_state(p, PSTATE_TERMINATING);
603 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_terminating, p);
604  
605 // schedule work
606 BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
607 }
608  
609 int process_have_child (struct process *p)
610 {
611 return (p->ap > 0 && p->statements[p->ap - 1].inst.istate == SSTATE_CHILD);
612 }
613  
614 void process_assert_pointers (struct process *p)
615 {
616 ASSERT(p->ap <= p->num_statements)
617 ASSERT(p->fp >= p->ap)
618 ASSERT(p->fp <= p->num_statements)
619  
620 #ifndef NDEBUG
621 // check AP
622 for (int i = 0; i < p->ap; i++) {
623 if (i == p->ap - 1) {
624 ASSERT(p->statements[i].inst.istate == SSTATE_ADULT || p->statements[i].inst.istate == SSTATE_CHILD)
625 } else {
626 ASSERT(p->statements[i].inst.istate == SSTATE_ADULT)
627 }
628 }
629  
630 // check FP
631 int fp = p->num_statements;
632 while (fp > 0 && p->statements[fp - 1].inst.istate == SSTATE_FORGOTTEN) {
633 fp--;
634 }
635 ASSERT(p->fp == fp)
636 #endif
637 }
638  
639 void process_logfunc (struct process *p)
640 {
641 BLog_Append("process %s: ", NCDInterpProcess_Name(p->iprocess));
642 }
643  
644 void process_log (struct process *p, int level, const char *fmt, ...)
645 {
646 va_list vl;
647 va_start(vl, fmt);
648 BLog_LogViaFuncVarArg((BLog_logfunc)process_logfunc, p, BLOG_CURRENT_CHANNEL, level, fmt, vl);
649 va_end(vl);
650 }
651  
652 void process_work_job_handler_working (struct process *p)
653 {
654 process_assert_pointers(p);
655 ASSERT(p->state == PSTATE_WORKING)
656  
657 // cleaning up?
658 if (p->ap < p->fp) {
659 // order the last living statement to die, if needed
660 struct statement *ps = &p->statements[p->fp - 1];
661 if (ps->inst.istate == SSTATE_DYING) {
662 return;
663 }
664  
665 STATEMENT_LOG(ps, BLOG_INFO, "killing");
666  
667 // set statement state DYING
668 ps->inst.istate = SSTATE_DYING;
669  
670 // order it to die
671 NCDModuleInst_Die(&ps->inst);
672 return;
673 }
674  
675 // clean?
676 if (process_have_child(p)) {
677 ASSERT(p->ap > 0)
678 ASSERT(p->ap <= p->num_statements)
679  
680 struct statement *ps = &p->statements[p->ap - 1];
681 ASSERT(ps->inst.istate == SSTATE_CHILD)
682  
683 STATEMENT_LOG(ps, BLOG_INFO, "clean");
684  
685 // report clean
686 NCDModuleInst_Clean(&ps->inst);
687 return;
688 }
689  
690 // finished?
691 if (p->ap == p->num_statements) {
692 process_log(p, BLOG_INFO, "victory");
693  
694 // set state up
695 process_set_state(p, PSTATE_UP);
696 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_up, p);
697  
698 // set module process up
699 if (p->module_process) {
700 NCDModuleProcess_Interp_Up(p->module_process);
701 }
702 return;
703 }
704  
705 // advancing?
706 struct statement *ps = &p->statements[p->ap];
707 ASSERT(ps->inst.istate == SSTATE_FORGOTTEN)
708  
709 if (p->error) {
710 STATEMENT_LOG(ps, BLOG_INFO, "waiting after error");
711  
712 // clear error
713 p->error = 0;
714  
715 // set wait timer
716 BReactor_SetSmallTimer(p->reactor, &p->wait_timer, BTIMER_SET_RELATIVE, p->interp->params.retry_time);
717 } else {
718 // advance
719 process_advance(p);
720 }
721 }
722  
723 void process_work_job_handler_up (struct process *p)
724 {
725 process_assert_pointers(p);
726 ASSERT(p->state == PSTATE_UP)
727 ASSERT(p->ap < p->num_statements || process_have_child(p))
728  
729 // if we have module process, wait for its permission to continue
730 if (p->module_process) {
731 // set state waiting
732 process_set_state(p, PSTATE_WAITING);
733 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_waiting, p);
734  
735 // set module process down
736 NCDModuleProcess_Interp_Down(p->module_process);
737 return;
738 }
739  
740 // set state working
741 process_set_state(p, PSTATE_WORKING);
742 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p);
743  
744 // delegate the rest to the working handler
745 process_work_job_handler_working(p);
746 }
747  
748 void process_work_job_handler_waiting (struct process *p)
749 {
750 process_assert_pointers(p);
751 ASSERT(p->state == PSTATE_WAITING)
752  
753 // do absolutely nothing. Having this no-op handler avoids a branch
754 // in statement_instance_func_event().
755 }
756  
757 void process_work_job_handler_terminating (struct process *p)
758 {
759 process_assert_pointers(p);
760 ASSERT(p->state == PSTATE_TERMINATING)
761  
762 again:
763 if (p->fp == 0) {
764 NCDInterpreter *interp = p->interp;
765  
766 // free process
767 NCDModuleProcess *mp;
768 process_free(p, &mp);
769  
770 // if program is terminating amd there are no more processes, exit program
771 if (interp->terminating && LinkedList1_IsEmpty(&interp->processes)) {
772 ASSERT(!mp)
773 interp->params.handler_finished(interp->params.user, interp->main_exit_code);
774 return;
775 }
776  
777 // inform the process creator that the process has terminated
778 if (mp) {
779 NCDModuleProcess_Interp_Terminated(mp);
780 return;
781 }
782  
783 return;
784 }
785  
786 // order the last living statement to die, if needed
787 struct statement *ps = &p->statements[p->fp - 1];
788 ASSERT(ps->inst.istate != SSTATE_FORGOTTEN)
789 if (ps->inst.istate == SSTATE_DYING) {
790 return;
791 }
792  
793 STATEMENT_LOG(ps, BLOG_INFO, "killing");
794  
795 // update AP
796 if (p->ap > ps->i) {
797 p->ap = ps->i;
798 }
799  
800 // optimize for statements which can be destroyed immediately
801 if (NCDModuleInst_TryFree(&ps->inst)) {
802 STATEMENT_LOG(ps, BLOG_INFO, "died");
803  
804 // free arguments memory
805 NCDValMem_Free(&ps->args_mem);
806  
807 // set statement state FORGOTTEN
808 ps->inst.istate = SSTATE_FORGOTTEN;
809  
810 // update FP
811 while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) {
812 p->fp--;
813 }
814  
815 goto again;
816 }
817  
818 // set statement state DYING
819 ps->inst.istate = SSTATE_DYING;
820  
821 // order it to die
822 NCDModuleInst_Die(&ps->inst);
823 return;
824 }
825  
826 int eval_func_eval_var (void *user, NCD_string_id_t const *varnames, size_t num_names, NCDValMem *mem, NCDValRef *out)
827 {
828 struct process *p = user;
829 ASSERT(varnames)
830 ASSERT(num_names > 0)
831 ASSERT(mem)
832 ASSERT(out)
833  
834 return process_resolve_variable_expr(p, p->ap, varnames, num_names, mem, out);
835 }
836  
837 static int eval_func_eval_call (void *user, NCD_string_id_t func_name_id, NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out)
838 {
839 struct process *p = user;
840 struct statement *ps = &p->statements[p->ap];
841  
842 struct NCDInterpFunction const *ifunc = NCDModuleIndex_FindFunction(&p->interp->mindex, func_name_id);
843 if (!ifunc) {
844 STATEMENT_LOG(ps, BLOG_ERROR, "unknown function: %s", NCDStringIndex_Value(&p->interp->string_index, func_name_id).ptr);
845 return 0;
846 }
847  
848 struct func_call_context context;
849 context.ps = ps;
850 context.args = &args;
851  
852 return NCDCall_DoIt(&p->interp->module_call_shared, &context, ifunc, NCDEvaluatorArgs_Count(&args), mem, out);
853 }
854  
855 void process_advance (struct process *p)
856 {
857 process_assert_pointers(p);
858 ASSERT(p->ap == p->fp)
859 ASSERT(!process_have_child(p))
860 ASSERT(p->ap < p->num_statements)
861 ASSERT(!p->error)
862 ASSERT(!BSmallPending_IsSet(&p->work_job))
863 ASSERT(p->state == PSTATE_WORKING)
864  
865 struct statement *ps = &p->statements[p->ap];
866 ASSERT(ps->inst.istate == SSTATE_FORGOTTEN)
867  
868 STATEMENT_LOG(ps, BLOG_INFO, "initializing");
869  
870 // need to determine the module and object to use it on (if it's a method)
871 const struct NCDInterpModule *module;
872 void *method_context = NULL;
873  
874 // get object names, e.g. "my.cat" in "my.cat->meow();"
875 // (or NULL if this is not a method statement)
876 const NCD_string_id_t *objnames;
877 size_t num_objnames;
878 NCDInterpProcess_StatementObjNames(p->iprocess, p->ap, &objnames, &num_objnames);
879  
880 if (!objnames) {
881 // not a method; module is already known by NCDInterpProcess
882 module = NCDInterpProcess_StatementGetSimpleModule(p->iprocess, p->ap, &p->interp->string_index, &p->interp->mindex);
883  
884 if (!module) {
885 const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index);
886 STATEMENT_LOG(ps, BLOG_ERROR, "unknown simple statement: %s", cmdname_str);
887 goto fail0;
888 }
889 } else {
890 // get object
891 NCDObject object;
892 if (!process_resolve_object_expr(p, p->ap, objnames, num_objnames, &object)) {
893 goto fail0;
894 }
895  
896 // get object type
897 NCD_string_id_t object_type = NCDObject_Type(&object);
898 if (object_type < 0) {
899 STATEMENT_LOG(ps, BLOG_ERROR, "cannot call method on object with no type");
900 goto fail0;
901 }
902  
903 // get method context
904 method_context = NCDObject_MethodUser(&object);
905  
906 // find module based on type of object
907 module = NCDInterpProcess_StatementGetMethodModule(p->iprocess, p->ap, object_type, &p->interp->mindex);
908  
909 if (!module) {
910 const char *type_str = NCDStringIndex_Value(&p->interp->string_index, object_type).ptr;
911 const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index);
912 STATEMENT_LOG(ps, BLOG_ERROR, "unknown method statement: %s::%s", type_str, cmdname_str);
913 goto fail0;
914 }
915 }
916  
917 // get evaluator expression for the arguments
918 NCDEvaluatorExpr *expr = NCDInterpProcess_GetStatementArgsExpr(p->iprocess, ps->i);
919  
920 // evaluate arguments
921 NCDValRef args;
922 NCDEvaluator_EvalFuncs funcs = {p, eval_func_eval_var, eval_func_eval_call};
923 if (!NCDEvaluatorExpr_Eval(expr, &p->interp->evaluator, &funcs, &ps->args_mem, &args)) {
924 STATEMENT_LOG(ps, BLOG_ERROR, "failed to evaluate arguments");
925 goto fail0;
926 }
927  
928 // allocate memory
929 if (!statement_allocate_memory(ps, module->module.alloc_size)) {
930 STATEMENT_LOG(ps, BLOG_ERROR, "failed to allocate memory");
931 goto fail1;
932 }
933  
934 // set statement state CHILD
935 ps->inst.istate = SSTATE_CHILD;
936  
937 // increment AP
938 p->ap++;
939  
940 // increment FP
941 p->fp++;
942  
943 process_assert_pointers(p);
944  
945 // initialize module instance
946 NCDModuleInst_Init(&ps->inst, module, method_context, args, &p->interp->module_params);
947 return;
948  
949 fail1:
950 NCDValMem_Free(&ps->args_mem);
951 fail0:
952 // set error
953 p->error = 1;
954  
955 // schedule work to start the timer
956 BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
957 }
958  
959 void process_wait_timer_handler (BSmallTimer *timer)
960 {
961 struct process *p = UPPER_OBJECT(timer, struct process, wait_timer);
962 process_assert_pointers(p);
963 ASSERT(!BSmallPending_IsSet(&p->work_job))
964  
965 // check if something happened that means we no longer need to retry
966 if (p->ap != p->fp || process_have_child(p) || p->ap == p->num_statements) {
967 return;
968 }
969  
970 process_log(p, BLOG_INFO, "retrying");
971  
972 // advance. Note: the asserts for this are indeed satisfied, though this
973 // it not trivial to prove.
974 process_advance(p);
975 }
976  
977 int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object)
978 {
979 ASSERT(pos >= 0)
980 ASSERT(pos <= p->num_statements)
981 ASSERT(out_object)
982  
983 int i = NCDInterpProcess_FindStatement(p->iprocess, pos, name);
984 if (i >= 0) {
985 struct statement *ps = &p->statements[i];
986 ASSERT(i < p->num_statements)
987  
988 if (ps->inst.istate == SSTATE_FORGOTTEN) {
989 process_log(p, BLOG_ERROR, "statement (%d) is uninitialized", i);
990 return 0;
991 }
992  
993 *out_object = NCDModuleInst_Object(&ps->inst);
994 return 1;
995 }
996  
997 if (p->module_process && NCDModuleProcess_Interp_GetSpecialObj(p->module_process, name, out_object)) {
998 return 1;
999 }
1000  
1001 return 0;
1002 }
1003  
1004 int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object)
1005 {
1006 ASSERT(pos >= 0)
1007 ASSERT(pos <= p->num_statements)
1008 ASSERT(names)
1009 ASSERT(num_names > 0)
1010 ASSERT(out_object)
1011  
1012 NCDObject object;
1013 if (!process_find_object(p, pos, names[0], &object)) {
1014 goto fail;
1015 }
1016  
1017 if (!NCDObject_ResolveObjExprCompact(&object, names + 1, num_names - 1, out_object)) {
1018 goto fail;
1019 }
1020  
1021 return 1;
1022  
1023 fail:;
1024 char *name = implode_id_strings(p->interp, names, num_names, '.');
1025 process_log(p, BLOG_ERROR, "failed to resolve object (%s) from position %zu", (name ? name : ""), pos);
1026 free(name);
1027 return 0;
1028 }
1029  
1030 int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value)
1031 {
1032 ASSERT(pos >= 0)
1033 ASSERT(pos <= p->num_statements)
1034 ASSERT(names)
1035 ASSERT(num_names > 0)
1036 ASSERT(mem)
1037 ASSERT(out_value)
1038  
1039 NCDObject object;
1040 if (!process_find_object(p, pos, names[0], &object)) {
1041 goto fail;
1042 }
1043  
1044 if (!NCDObject_ResolveVarExprCompact(&object, names + 1, num_names - 1, mem, out_value)) {
1045 goto fail;
1046 }
1047  
1048 return 1;
1049  
1050 fail:;
1051 char *name = implode_id_strings(p->interp, names, num_names, '.');
1052 process_log(p, BLOG_ERROR, "failed to resolve variable (%s) from position %zu", (name ? name : ""), pos);
1053 free(name);
1054 return 0;
1055 }
1056  
1057 void statement_logfunc (struct statement *ps)
1058 {
1059 process_logfunc(statement_process(ps));
1060 BLog_Append("statement %zu: ", ps->i);
1061 }
1062  
1063 void statement_log (struct statement *ps, int level, const char *fmt, ...)
1064 {
1065 va_list vl;
1066 va_start(vl, fmt);
1067 BLog_LogViaFuncVarArg((BLog_logfunc)statement_logfunc, ps, BLOG_CURRENT_CHANNEL, level, fmt, vl);
1068 va_end(vl);
1069 }
1070  
1071 struct process * statement_process (struct statement *ps)
1072 {
1073 return UPPER_OBJECT(ps - ps->i, struct process, statements);
1074 }
1075  
1076 int statement_mem_is_allocated (struct statement *ps)
1077 {
1078 return (ps->mem_size < 0);
1079 }
1080  
1081 int statement_mem_size (struct statement *ps)
1082 {
1083 return (ps->mem_size >= 0 ? ps->mem_size : -ps->mem_size);
1084 }
1085  
1086 int statement_allocate_memory (struct statement *ps, int alloc_size)
1087 {
1088 ASSERT(alloc_size >= 0)
1089  
1090 if (alloc_size > statement_mem_size(ps)) {
1091 // allocate new memory
1092 char *new_mem = malloc(alloc_size);
1093 if (!new_mem) {
1094 STATEMENT_LOG(ps, BLOG_ERROR, "malloc failed");
1095 return 0;
1096 }
1097  
1098 // release old memory unless it was preallocated
1099 if (statement_mem_is_allocated(ps)) {
1100 free(ps->inst.mem);
1101 }
1102  
1103 struct process *p = statement_process(ps);
1104  
1105 // register memory in statement
1106 ps->inst.mem = new_mem;
1107 ps->mem_size = -alloc_size;
1108  
1109 // set the alloc flag in the process to make sure process_free()
1110 // releases the allocated memory
1111 p->have_alloc = 1;
1112  
1113 // register alloc size for future preallocations
1114 NCDInterpProcess_StatementBumpAllocSize(p->iprocess, ps->i, alloc_size);
1115 }
1116  
1117 return 1;
1118 }
1119  
1120 void statement_instance_func_event (NCDModuleInst *inst, int event)
1121 {
1122 struct statement *ps = UPPER_OBJECT(inst, struct statement, inst);
1123 ASSERT(ps->inst.istate == SSTATE_CHILD || ps->inst.istate == SSTATE_ADULT || ps->inst.istate == SSTATE_DYING)
1124  
1125 struct process *p = statement_process(ps);
1126 process_assert_pointers(p);
1127  
1128 // schedule work
1129 BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
1130  
1131 switch (event) {
1132 case NCDMODULE_EVENT_UP: {
1133 ASSERT(ps->inst.istate == SSTATE_CHILD)
1134  
1135 STATEMENT_LOG(ps, BLOG_INFO, "up");
1136  
1137 // set state ADULT
1138 ps->inst.istate = SSTATE_ADULT;
1139 } break;
1140  
1141 case NCDMODULE_EVENT_DOWN: {
1142 ASSERT(ps->inst.istate == SSTATE_ADULT)
1143  
1144 STATEMENT_LOG(ps, BLOG_INFO, "down");
1145  
1146 // set state CHILD
1147 ps->inst.istate = SSTATE_CHILD;
1148  
1149 // clear error
1150 if (ps->i < p->ap) {
1151 p->error = 0;
1152 }
1153  
1154 // update AP
1155 if (p->ap > ps->i + 1) {
1156 p->ap = ps->i + 1;
1157 }
1158 } break;
1159  
1160 case NCDMODULE_EVENT_DOWNUP: {
1161 ASSERT(ps->inst.istate == SSTATE_ADULT)
1162  
1163 STATEMENT_LOG(ps, BLOG_INFO, "down");
1164 STATEMENT_LOG(ps, BLOG_INFO, "up");
1165  
1166 // clear error
1167 if (ps->i < p->ap) {
1168 p->error = 0;
1169 }
1170  
1171 // update AP
1172 if (p->ap > ps->i + 1) {
1173 p->ap = ps->i + 1;
1174 }
1175 } break;
1176  
1177 case NCDMODULE_EVENT_DEAD: {
1178 STATEMENT_LOG(ps, BLOG_INFO, "died");
1179  
1180 // free instance
1181 NCDModuleInst_Free(&ps->inst);
1182  
1183 // free arguments memory
1184 NCDValMem_Free(&ps->args_mem);
1185  
1186 // set state FORGOTTEN
1187 ps->inst.istate = SSTATE_FORGOTTEN;
1188  
1189 // update AP
1190 if (p->ap > ps->i) {
1191 p->ap = ps->i;
1192 }
1193  
1194 // update FP
1195 while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) {
1196 p->fp--;
1197 }
1198 } break;
1199  
1200 case NCDMODULE_EVENT_DEADERROR: {
1201 STATEMENT_LOG(ps, BLOG_ERROR, "died with error");
1202  
1203 // free instance
1204 NCDModuleInst_Free(&ps->inst);
1205  
1206 // free arguments memory
1207 NCDValMem_Free(&ps->args_mem);
1208  
1209 // set state FORGOTTEN
1210 ps->inst.istate = SSTATE_FORGOTTEN;
1211  
1212 // set error
1213 if (ps->i < p->ap) {
1214 p->error = 1;
1215 }
1216  
1217 // update AP
1218 if (p->ap > ps->i) {
1219 p->ap = ps->i;
1220 }
1221  
1222 // update FP
1223 while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) {
1224 p->fp--;
1225 }
1226 } break;
1227 }
1228 }
1229  
1230 int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object)
1231 {
1232 struct statement *ps = UPPER_OBJECT(inst, struct statement, inst);
1233 ASSERT(ps->inst.istate != SSTATE_FORGOTTEN)
1234  
1235 return process_find_object(statement_process(ps), ps->i, objname, out_object);
1236 }
1237  
1238 int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess* mp, NCD_string_id_t template_name)
1239 {
1240 NCDInterpreter *interp = vinterp;
1241  
1242 // find process
1243 NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, template_name);
1244 if (!iprocess) {
1245 const char *str = NCDStringIndex_Value(&interp->string_index, template_name).ptr;
1246 BLog(BLOG_ERROR, "no template named %s", str);
1247 return 0;
1248 }
1249  
1250 // make sure it's a template
1251 if (!NCDInterpProcess_IsTemplate(iprocess)) {
1252 const char *str = NCDStringIndex_Value(&interp->string_index, template_name).ptr;
1253 BLog(BLOG_ERROR, "need template to create a process, but %s is a process", str);
1254 return 0;
1255 }
1256  
1257 // create process
1258 if (!process_new(interp, iprocess, mp)) {
1259 const char *str = NCDStringIndex_Value(&interp->string_index, template_name).ptr;
1260 BLog(BLOG_ERROR, "failed to create process from template %s", str);
1261 return 0;
1262 }
1263  
1264 if (BLog_WouldLog(BLOG_INFO, BLOG_CURRENT_CHANNEL)) {
1265 const char *str = NCDStringIndex_Value(&interp->string_index, template_name).ptr;
1266 BLog(BLOG_INFO, "created process from template %s", str);
1267 }
1268  
1269 return 1;
1270 }
1271  
1272 void statement_instance_logfunc (NCDModuleInst *inst)
1273 {
1274 struct statement *ps = UPPER_OBJECT(inst, struct statement, inst);
1275 ASSERT(ps->inst.istate != SSTATE_FORGOTTEN)
1276  
1277 statement_logfunc(ps);
1278 BLog_Append("module: ");
1279 }
1280  
1281 void statement_instance_func_interp_exit (void *vinterp, int exit_code)
1282 {
1283 NCDInterpreter *interp = vinterp;
1284  
1285 start_terminate(interp, exit_code);
1286 }
1287  
1288 int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value)
1289 {
1290 NCDInterpreter *interp = vinterp;
1291  
1292 *out_value = NCDVal_NewList(mem, interp->params.num_extra_args);
1293 if (NCDVal_IsInvalid(*out_value)) {
1294 BLog(BLOG_ERROR, "NCDVal_NewList failed");
1295 goto fail;
1296 }
1297  
1298 for (int i = 0; i < interp->params.num_extra_args; i++) {
1299 NCDValRef arg = NCDVal_NewString(mem, interp->params.extra_args[i]);
1300 if (NCDVal_IsInvalid(arg)) {
1301 BLog(BLOG_ERROR, "NCDVal_NewString failed");
1302 goto fail;
1303 }
1304  
1305 if (!NCDVal_ListAppend(*out_value, arg)) {
1306 BLog(BLOG_ERROR, "depth limit exceeded");
1307 goto fail;
1308 }
1309 }
1310  
1311 return 1;
1312  
1313 fail:
1314 *out_value = NCDVal_NewInvalid();
1315 return 1;
1316 }
1317  
1318 btime_t statement_instance_func_interp_getretrytime (void *vinterp)
1319 {
1320 NCDInterpreter *interp = vinterp;
1321  
1322 return interp->params.retry_time;
1323 }
1324  
1325 int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group)
1326 {
1327 NCDInterpreter *interp = vinterp;
1328  
1329 if (!NCDModuleIndex_AddGroup(&interp->mindex, group, &interp->module_iparams, &interp->string_index)) {
1330 BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed");
1331 return 0;
1332 }
1333  
1334 return 1;
1335 }
1336  
1337 void process_moduleprocess_func_event (struct process *p, int event)
1338 {
1339 ASSERT(p->module_process)
1340  
1341 switch (event) {
1342 case NCDMODULEPROCESS_INTERP_EVENT_CONTINUE: {
1343 ASSERT(p->state == PSTATE_WAITING)
1344  
1345 // set state working
1346 process_set_state(p, PSTATE_WORKING);
1347 BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p);
1348  
1349 // schedule work
1350 BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
1351 } break;
1352  
1353 case NCDMODULEPROCESS_INTERP_EVENT_TERMINATE: {
1354 ASSERT(p->state != PSTATE_TERMINATING)
1355  
1356 process_log(p, BLOG_INFO, "process termination requested");
1357  
1358 // start terminating
1359 process_start_terminating(p);
1360 } break;
1361  
1362 default: ASSERT(0);
1363 }
1364 }
1365  
1366 int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object)
1367 {
1368 ASSERT(p->module_process)
1369  
1370 return process_find_object(p, p->num_statements, name, out_object);
1371 }
1372  
1373 void function_logfunc (void *user)
1374 {
1375 struct func_call_context const *context = user;
1376 ASSERT(context->ps->inst.istate == SSTATE_FORGOTTEN)
1377  
1378 statement_logfunc(context->ps);
1379 BLog_Append("func_eval: ");
1380 }
1381  
1382 int function_eval_arg (void *user, size_t index, NCDValMem *mem, NCDValRef *out)
1383 {
1384 struct func_call_context const *context = user;
1385 ASSERT(context->ps->inst.istate == SSTATE_FORGOTTEN)
1386  
1387 return NCDEvaluatorArgs_EvalArg(context->args, index, mem, out);
1388 }