BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file foreach.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Synopsis:
32 * foreach(list/map collection, string template, list args)
33 *
34 * Description:
35 * Initializes a template process for each element of list, sequentially,
36 * obeying to the usual execution model of NCD.
37 * It's equivalent to (except for special variables):
38 *
39 * call(template, args);
40 * ...
41 * call(template, args); # one call() for every element of list
42 *
43 * Template process specials:
44 *
45 * _index - (lists only) index of the list element corresponding to the template,
46 * process, as a decimal string, starting from zero
47 * _elem - (lists only) element of list corresponding to the template process
48 * _key - (maps only) key of the current map entry
49 * _val - (maps only) value of the current map entry
50 * _caller.X - X as seen from the foreach() statement
51 *
52 * Synopsis:
53 * foreach_emb(list/map collection, string template, string name1 [, string name2])
54 *
55 * Description:
56 * Foreach for embedded templates; the desugaring process converts Foreach
57 * clauses into this statement. The called templates have direct access to
58 * objects as seen from this statement, and also some kind of access to the
59 * current element of the iteration, depending on the type of collection
60 * being iterated, and whether 'name2' is provided:
61 * List and one name: current element is named 'name1'.
62 * List and both names: current index is named 'name1', current element 'name2'.
63 * Map and one name: current key is named 'name1'.
64 * Map and both names: current key is named 'name1', current value 'name2'.
65 */
66  
67 #include <stdlib.h>
68 #include <string.h>
69 #include <limits.h>
70  
71 #include <misc/balloc.h>
72 #include <misc/string_begins_with.h>
73 #include <misc/debug.h>
74 #include <misc/offset.h>
75 #include <system/BReactor.h>
76  
77 #include <ncd/module_common.h>
78  
79 #include <generated/blog_channel_ncd_foreach.h>
80  
81 #define ISTATE_WORKING 1
82 #define ISTATE_UP 2
83 #define ISTATE_WAITING 3
84 #define ISTATE_TERMINATING 4
85  
86 #define ESTATE_FORGOTTEN 1
87 #define ESTATE_DOWN 2
88 #define ESTATE_UP 3
89 #define ESTATE_WAITING 4
90 #define ESTATE_TERMINATING 5
91  
92 struct element;
93  
94 struct instance {
95 NCDModuleInst *i;
96 NCDValRef template_name;
97 NCDValRef args;
98 NCD_string_id_t name1;
99 NCD_string_id_t name2;
100 BTimer timer;
101 struct element *elems;
102 int type;
103 int num_elems;
104 int gp; // good pointer
105 int ip; // initialized pointer
106 int state;
107 };
108  
109 struct element {
110 struct instance *inst;
111 union {
112 struct {
113 NCDValRef list_elem;
114 };
115 struct {
116 NCDValRef map_key;
117 NCDValRef map_val;
118 };
119 };
120 NCDModuleProcess process;
121 int i;
122 int state;
123 };
124  
125 static void assert_state (struct instance *o);
126 static void work (struct instance *o);
127 static void advance (struct instance *o);
128 static void timer_handler (struct instance *o);
129 static void element_process_handler_event (NCDModuleProcess *process, int event);
130 static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
131 static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
132 static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
133 static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
134 static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
135 static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
136 static void instance_free (struct instance *o);
137  
138 enum {STRING_INDEX, STRING_ELEM, STRING_KEY, STRING_VAL};
139  
140 static const char *strings[] = {
141 "_index", "_elem", "_key", "_val", NULL
142 };
143  
144 static void assert_state (struct instance *o)
145 {
146 ASSERT(o->num_elems >= 0)
147 ASSERT(o->gp >= 0)
148 ASSERT(o->ip >= 0)
149 ASSERT(o->gp <= o->num_elems)
150 ASSERT(o->ip <= o->num_elems)
151 ASSERT(o->gp <= o->ip)
152  
153 #ifndef NDEBUG
154 // check GP
155 for (int i = 0; i < o->gp; i++) {
156 if (i == o->gp - 1) {
157 ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN ||
158 o->elems[i].state == ESTATE_WAITING)
159 } else {
160 ASSERT(o->elems[i].state == ESTATE_UP)
161 }
162 }
163  
164 // check IP
165 int ip = o->num_elems;
166 while (ip > 0 && o->elems[ip - 1].state == ESTATE_FORGOTTEN) {
167 ip--;
168 }
169 ASSERT(o->ip == ip)
170  
171 // check gap
172 for (int i = o->gp; i < o->ip; i++) {
173 if (i == o->ip - 1) {
174 ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN ||
175 o->elems[i].state == ESTATE_WAITING || o->elems[i].state == ESTATE_TERMINATING)
176 } else {
177 ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN ||
178 o->elems[i].state == ESTATE_WAITING)
179 }
180 }
181 #endif
182 }
183  
184 static void work (struct instance *o)
185 {
186 assert_state(o);
187  
188 // stop timer
189 BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer);
190  
191 if (o->state == ISTATE_WAITING) {
192 return;
193 }
194  
195 if (o->state == ISTATE_UP && !(o->gp == o->ip && o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP))) {
196 // signal down
197 NCDModuleInst_Backend_Down(o->i);
198  
199 // set state waiting
200 o->state = ISTATE_WAITING;
201 return;
202 }
203  
204 if (o->gp < o->ip) {
205 // get last element
206 struct element *le = &o->elems[o->ip - 1];
207 ASSERT(le->state != ESTATE_FORGOTTEN)
208  
209 // start terminating if not already
210 if (le->state != ESTATE_TERMINATING) {
211 // request termination
212 NCDModuleProcess_Terminate(&le->process);
213  
214 // set element state terminating
215 le->state = ESTATE_TERMINATING;
216 }
217  
218 return;
219 }
220  
221 if (o->state == ISTATE_TERMINATING) {
222 // finally die
223 instance_free(o);
224 return;
225 }
226  
227 if (o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)) {
228 if (o->state == ISTATE_WORKING) {
229 // signal up
230 NCDModuleInst_Backend_Up(o->i);
231  
232 // set state up
233 o->state = ISTATE_UP;
234 }
235  
236 return;
237 }
238  
239 if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_WAITING) {
240 // get last element
241 struct element *le = &o->elems[o->gp - 1];
242  
243 // continue process
244 NCDModuleProcess_Continue(&le->process);
245  
246 // set state down
247 le->state = ESTATE_DOWN;
248 return;
249 }
250  
251 if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_DOWN) {
252 return;
253 }
254  
255 ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)
256  
257 advance(o);
258 return;
259 }
260  
261 static void advance (struct instance *o)
262 {
263 assert_state(o);
264 ASSERT(o->gp == o->ip)
265 ASSERT(o->gp < o->num_elems)
266 ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)
267 ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN)
268  
269 // get next element
270 struct element *e = &o->elems[o->gp];
271  
272 // init process
273 if (!NCDModuleProcess_InitValue(&e->process, o->i, o->template_name, o->args, element_process_handler_event)) {
274 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
275 goto fail;
276 }
277  
278 // set special functions
279 NCDModuleProcess_SetSpecialFuncs(&e->process, element_process_func_getspecialobj);
280  
281 // set element state down
282 e->state = ESTATE_DOWN;
283  
284 // increment GP and IP
285 o->gp++;
286 o->ip++;
287 return;
288  
289 fail:
290 // set timer
291 BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer);
292 }
293  
294 static void timer_handler (struct instance *o)
295 {
296 assert_state(o);
297 ASSERT(o->gp == o->ip)
298 ASSERT(o->gp < o->num_elems)
299 ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)
300 ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN)
301  
302 advance(o);
303 return;
304 }
305  
306 static void element_process_handler_event (NCDModuleProcess *process, int event)
307 {
308 struct element *e = UPPER_OBJECT(process, struct element, process);
309 struct instance *o = e->inst;
310 assert_state(o);
311 ASSERT(e->i < o->ip)
312 ASSERT(e->state != ESTATE_FORGOTTEN)
313  
314 switch (event) {
315 case NCDMODULEPROCESS_EVENT_UP: {
316 ASSERT(e->state == ESTATE_DOWN)
317 ASSERT(o->gp == o->ip)
318 ASSERT(o->gp == e->i + 1)
319  
320 // set element state up
321 e->state = ESTATE_UP;
322 } break;
323  
324 case NCDMODULEPROCESS_EVENT_DOWN: {
325 ASSERT(e->state == ESTATE_UP)
326  
327 // set element state waiting
328 e->state = ESTATE_WAITING;
329  
330 // bump down GP
331 if (o->gp > e->i + 1) {
332 o->gp = e->i + 1;
333 }
334 } break;
335  
336 case NCDMODULEPROCESS_EVENT_TERMINATED: {
337 ASSERT(e->state == ESTATE_TERMINATING)
338 ASSERT(o->gp < o->ip)
339 ASSERT(o->ip == e->i + 1)
340  
341 // free process
342 NCDModuleProcess_Free(&e->process);
343  
344 // set element state forgotten
345 e->state = ESTATE_FORGOTTEN;
346  
347 // decrement IP
348 o->ip--;
349 } break;
350  
351 default: ASSERT(0);
352 }
353  
354 work(o);
355 return;
356 }
357  
358 static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
359 {
360 struct element *e = UPPER_OBJECT(process, struct element, process);
361 struct instance *o = e->inst;
362 ASSERT(e->state != ESTATE_FORGOTTEN)
363  
364 switch (o->type) {
365 case NCDVAL_LIST: {
366 NCD_string_id_t index_name = (o->name2 >= 0 ? o->name1 : -1);
367 NCD_string_id_t elem_name = (o->name2 >= 0 ? o->name2 : o->name1);
368  
369 if (index_name >= 0 && name == index_name) {
370 *out_object = NCDObject_Build(-1, e, element_list_index_object_func_getvar, NCDObject_no_getobj);
371 return 1;
372 }
373  
374 if (name == elem_name) {
375 *out_object = NCDObject_Build(-1, e, element_list_elem_object_func_getvar, NCDObject_no_getobj);
376 return 1;
377 }
378 } break;
379 case NCDVAL_MAP: {
380 NCD_string_id_t key_name = o->name1;
381 NCD_string_id_t val_name = o->name2;
382  
383 if (name == key_name) {
384 *out_object = NCDObject_Build(-1, e, element_map_key_object_func_getvar, NCDObject_no_getobj);
385 return 1;
386 }
387  
388 if (val_name >= 0 && name == val_name) {
389 *out_object = NCDObject_Build(-1, e, element_map_val_object_func_getvar, NCDObject_no_getobj);
390 return 1;
391 }
392 } break;
393 }
394  
395 if (NCDVal_IsInvalid(o->args)) {
396 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
397 }
398  
399 if (name == NCD_STRING_CALLER) {
400 *out_object = NCDObject_Build(-1, e, NCDObject_no_getvar, element_caller_object_func_getobj);
401 return 1;
402 }
403  
404 return 0;
405 }
406  
407 static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
408 {
409 struct element *e = NCDObject_DataPtr(obj);
410 struct instance *o = e->inst;
411 ASSERT(e->state != ESTATE_FORGOTTEN)
412 ASSERT(!NCDVal_IsInvalid(o->args))
413  
414 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
415 }
416  
417 static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
418 {
419 struct element *e = NCDObject_DataPtr(obj);
420 struct instance *o = e->inst;
421 B_USE(o)
422 ASSERT(e->state != ESTATE_FORGOTTEN)
423 ASSERT(o->type == NCDVAL_LIST)
424  
425 if (name != NCD_STRING_EMPTY) {
426 return 0;
427 }
428  
429 *out = ncd_make_uintmax(mem, e->i);
430 return 1;
431 }
432  
433 static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
434 {
435 struct element *e = NCDObject_DataPtr(obj);
436 struct instance *o = e->inst;
437 B_USE(o)
438 ASSERT(e->state != ESTATE_FORGOTTEN)
439 ASSERT(o->type == NCDVAL_LIST)
440  
441 if (name != NCD_STRING_EMPTY) {
442 return 0;
443 }
444  
445 *out = NCDVal_NewCopy(mem, e->list_elem);
446 return 1;
447 }
448  
449 static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
450 {
451 struct element *e = NCDObject_DataPtr(obj);
452 struct instance *o = e->inst;
453 B_USE(o)
454 ASSERT(e->state != ESTATE_FORGOTTEN)
455 ASSERT(o->type == NCDVAL_MAP)
456  
457 if (name != NCD_STRING_EMPTY) {
458 return 0;
459 }
460  
461 *out = NCDVal_NewCopy(mem, e->map_key);
462 return 1;
463 }
464  
465 static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
466 {
467 struct element *e = NCDObject_DataPtr(obj);
468 struct instance *o = e->inst;
469 B_USE(o)
470 ASSERT(e->state != ESTATE_FORGOTTEN)
471 ASSERT(o->type == NCDVAL_MAP)
472  
473 if (name != NCD_STRING_EMPTY) {
474 return 0;
475 }
476  
477 *out = NCDVal_NewCopy(mem, e->map_val);
478 return 1;
479 }
480  
481 static void func_new_common (void *vo, NCDModuleInst *i, NCDValRef collection, NCDValRef template_name, NCDValRef args, NCD_string_id_t name1, NCD_string_id_t name2)
482 {
483 ASSERT(!NCDVal_IsInvalid(collection))
484 ASSERT(NCDVal_IsString(template_name))
485 ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args))
486 ASSERT(name1 >= 0)
487  
488 struct instance *o = vo;
489 o->i = i;
490  
491 o->type = NCDVal_Type(collection);
492 o->template_name = template_name;
493 o->args = args;
494 o->name1 = name1;
495 o->name2 = name2;
496  
497 // init timer
498 btime_t retry_time = NCDModuleInst_Backend_InterpGetRetryTime(i);
499 BTimer_Init(&o->timer, retry_time, (BTimer_handler)timer_handler, o);
500  
501 size_t num_elems;
502 NCDValMapElem cur_map_elem;
503  
504 switch (o->type) {
505 case NCDVAL_LIST: {
506 num_elems = NCDVal_ListCount(collection);
507 } break;
508 case NCDVAL_MAP: {
509 num_elems = NCDVal_MapCount(collection);
510 cur_map_elem = NCDVal_MapOrderedFirst(collection);
511 } break;
512 default:
513 ModuleLog(i, BLOG_ERROR, "invalid collection type");
514 goto fail0;
515 }
516  
517 if (num_elems > INT_MAX) {
518 ModuleLog(i, BLOG_ERROR, "too many elements");
519 goto fail0;
520 }
521 o->num_elems = num_elems;
522  
523 // allocate elements
524 if (!(o->elems = BAllocArray(o->num_elems, sizeof(o->elems[0])))) {
525 ModuleLog(i, BLOG_ERROR, "BAllocArray failed");
526 goto fail0;
527 }
528  
529 for (int j = 0; j < o->num_elems; j++) {
530 struct element *e = &o->elems[j];
531  
532 // set instance
533 e->inst = o;
534  
535 // set index
536 e->i = j;
537  
538 // set state forgotten
539 e->state = ESTATE_FORGOTTEN;
540  
541 // set values
542 switch (o->type) {
543 case NCDVAL_LIST: {
544 e->list_elem = NCDVal_ListGet(collection, j);
545 } break;
546 case NCDVAL_MAP: {
547 e->map_key = NCDVal_MapElemKey(collection, cur_map_elem);
548 e->map_val = NCDVal_MapElemVal(collection, cur_map_elem);
549 cur_map_elem = NCDVal_MapOrderedNext(collection, cur_map_elem);
550 } break;
551 }
552 }
553  
554 // set GP and IP zero
555 o->gp = 0;
556 o->ip = 0;
557  
558 // set state working
559 o->state = ISTATE_WORKING;
560  
561 work(o);
562 return;
563  
564 fail0:
565 NCDModuleInst_Backend_DeadError(i);
566 }
567  
568 static void func_new_foreach (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
569 {
570 // read arguments
571 NCDValRef arg_collection;
572 NCDValRef arg_template;
573 NCDValRef arg_args;
574 if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_args)) {
575 ModuleLog(i, BLOG_ERROR, "wrong arity");
576 goto fail0;
577 }
578 if (!NCDVal_IsString(arg_template) || !NCDVal_IsList(arg_args)) {
579 ModuleLog(i, BLOG_ERROR, "wrong type");
580 goto fail0;
581 }
582  
583 NCD_string_id_t name1;
584 NCD_string_id_t name2;
585  
586 switch (NCDVal_Type(arg_collection)) {
587 case NCDVAL_LIST: {
588 name1 = ModuleString(i, STRING_INDEX);
589 name2 = ModuleString(i, STRING_ELEM);
590 } break;
591 case NCDVAL_MAP: {
592 name1 = ModuleString(i, STRING_KEY);
593 name2 = ModuleString(i, STRING_VAL);
594 } break;
595 default:
596 ModuleLog(i, BLOG_ERROR, "invalid collection type");
597 goto fail0;
598 }
599  
600 func_new_common(vo, i, arg_collection, arg_template, arg_args, name1, name2);
601 return;
602  
603 fail0:
604 NCDModuleInst_Backend_DeadError(i);
605 }
606  
607 static void func_new_foreach_emb (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
608 {
609 // read arguments
610 NCDValRef arg_collection;
611 NCDValRef arg_template;
612 NCDValRef arg_name1;
613 NCDValRef arg_name2 = NCDVal_NewInvalid();
614 if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_name1) && !NCDVal_ListRead(params->args, 4, &arg_collection, &arg_template, &arg_name1, &arg_name2)) {
615 ModuleLog(i, BLOG_ERROR, "wrong arity");
616 goto fail0;
617 }
618 if (!NCDVal_IsString(arg_template) || !NCDVal_IsString(arg_name1) || (!NCDVal_IsInvalid(arg_name2) && !NCDVal_IsString(arg_name2))) {
619 ModuleLog(i, BLOG_ERROR, "wrong type");
620 goto fail0;
621 }
622  
623 NCD_string_id_t name1 = ncd_get_string_id(arg_name1);
624 if (name1 < 0) {
625 ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed");
626 goto fail0;
627 }
628  
629 NCD_string_id_t name2 = -1;
630 if (!NCDVal_IsInvalid(arg_name2)) {
631 name2 = ncd_get_string_id(arg_name2);
632 if (name2 < 0) {
633 ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed");
634 goto fail0;
635 }
636 }
637  
638 func_new_common(vo, i, arg_collection, arg_template, NCDVal_NewInvalid(), name1, name2);
639 return;
640  
641 fail0:
642 NCDModuleInst_Backend_DeadError(i);
643 }
644  
645 static void instance_free (struct instance *o)
646 {
647 ASSERT(o->gp == 0)
648 ASSERT(o->ip == 0)
649  
650 // free elements
651 BFree(o->elems);
652  
653 // free timer
654 BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer);
655  
656 NCDModuleInst_Backend_Dead(o->i);
657 }
658  
659 static void func_die (void *vo)
660 {
661 struct instance *o = vo;
662 assert_state(o);
663 ASSERT(o->state != ISTATE_TERMINATING)
664  
665 // set GP zero
666 o->gp = 0;
667  
668 // set state terminating
669 o->state = ISTATE_TERMINATING;
670  
671 work(o);
672 return;
673 }
674  
675 static void func_clean (void *vo)
676 {
677 struct instance *o = vo;
678  
679 if (o->state != ISTATE_WAITING) {
680 return;
681 }
682  
683 // set state working
684 o->state = ISTATE_WORKING;
685  
686 work(o);
687 return;
688 }
689  
690 static struct NCDModule modules[] = {
691 {
692 .type = "foreach",
693 .func_new2 = func_new_foreach,
694 .func_die = func_die,
695 .func_clean = func_clean,
696 .alloc_size = sizeof(struct instance)
697 }, {
698 .type = "foreach_emb",
699 .func_new2 = func_new_foreach_emb,
700 .func_die = func_die,
701 .func_clean = func_clean,
702 .alloc_size = sizeof(struct instance)
703 }, {
704 .type = NULL
705 }
706 };
707  
708 const struct NCDModuleGroup ncdmodule_foreach = {
709 .modules = modules,
710 .strings = strings
711 };