BadVPN – Blame information for rev 1
?pathlinks?
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 | } |