BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file multidepend.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 * This is a compatibility module. It behaves exactly like the depend_scope module,
32 * except that there is a single global scope for dependency names.
33 *
34 * Use depend_scope instead. If you are using multidepend between non-template
35 * processes, make those processes templates instead and start them via
36 * process_manager(). For example, instead of this:
37 *
38 * process foo {
39 * multiprovide("FOO");
40 * }
41 *
42 * process bar {
43 * multidepend({"FOO"});
44 * }
45 *
46 * Use this:
47 *
48 * process main {
49 * depend_scope() scope;
50 * process_manager() mgr;
51 * mgr->start("foo", "foo", {});
52 * mgr->start("bar", "bar", {});
53 * }
54 *
55 * template foo {
56 * _caller.scope->provide("FOO");
57 * }
58 *
59 * template bar {
60 * _caller.scope->depend({"FOO"});
61 * }
62 *
63 * Synopsis:
64 * multiprovide(name)
65 *
66 * Synopsis:
67 * multidepend(list names)
68 */
69  
70 #include <stdlib.h>
71 #include <string.h>
72  
73 #include <misc/offset.h>
74 #include <misc/debug.h>
75 #include <misc/balloc.h>
76 #include <structure/LinkedList1.h>
77  
78 #include <ncd/module_common.h>
79  
80 #include <generated/blog_channel_ncd_multidepend.h>
81  
82 struct provide {
83 NCDModuleInst *i;
84 NCDValRef name;
85 LinkedList1Node provides_list_node;
86 LinkedList1 depends_list;
87 int dying;
88 };
89  
90 struct depend {
91 NCDModuleInst *i;
92 NCDValRef names;
93 LinkedList1Node depends_list_node;
94 struct provide *provide;
95 LinkedList1Node provide_depends_list_node;
96 int provide_collapsing;
97 };
98  
99 struct global {
100 LinkedList1 provides_list;
101 LinkedList1 depends_list;
102 };
103  
104 static struct provide * find_provide (struct global *g, NCDValRef name)
105 {
106 for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->provides_list); ln; ln = LinkedList1Node_Next(ln)) {
107 struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node);
108 if (NCDVal_Compare(provide->name, name) == 0) {
109 return provide;
110 }
111 }
112  
113 return NULL;
114 }
115  
116 static struct provide * depend_find_best_provide (struct depend *o)
117 {
118 struct global *g = ModuleGlobal(o->i);
119  
120 size_t count = NCDVal_ListCount(o->names);
121  
122 for (size_t j = 0; j < count; j++) {
123 NCDValRef name = NCDVal_ListGet(o->names, j);
124 struct provide *provide = find_provide(g, name);
125 if (provide && !provide->dying) {
126 return provide;
127 }
128 }
129  
130 return NULL;
131 }
132  
133 static void depend_update (struct depend *o)
134 {
135 // if we're collapsing, do nothing
136 if (o->provide && o->provide_collapsing) {
137 return;
138 }
139  
140 // find best provide
141 struct provide *best_provide = depend_find_best_provide(o);
142 ASSERT(!best_provide || !best_provide->dying)
143  
144 // has anything changed?
145 if (best_provide == o->provide) {
146 return;
147 }
148  
149 if (o->provide) {
150 // set collapsing
151 o->provide_collapsing = 1;
152  
153 // signal down
154 NCDModuleInst_Backend_Down(o->i);
155 } else {
156 // insert to provide's list
157 LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node);
158  
159 // set not collapsing
160 o->provide_collapsing = 0;
161  
162 // set provide
163 o->provide = best_provide;
164  
165 // signal up
166 NCDModuleInst_Backend_Up(o->i);
167 }
168 }
169  
170 static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params)
171 {
172 // allocate global state structure
173 struct global *g = BAlloc(sizeof(*g));
174 if (!g) {
175 BLog(BLOG_ERROR, "BAlloc failed");
176 return 0;
177 }
178  
179 // set group state pointer
180 group->group_state = g;
181  
182 // init provides list
183 LinkedList1_Init(&g->provides_list);
184  
185 // init depends list
186 LinkedList1_Init(&g->depends_list);
187  
188 return 1;
189 }
190  
191 static void func_globalfree (struct NCDInterpModuleGroup *group)
192 {
193 struct global *g = group->group_state;
194 ASSERT(LinkedList1_IsEmpty(&g->depends_list))
195 ASSERT(LinkedList1_IsEmpty(&g->provides_list))
196  
197 // free global state structure
198 BFree(g);
199 }
200  
201 static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
202 {
203 struct global *g = ModuleGlobal(i);
204 struct provide *o = vo;
205 o->i = i;
206  
207 // read arguments
208 NCDValRef name_arg;
209 if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
210 ModuleLog(i, BLOG_ERROR, "wrong arity");
211 goto fail0;
212 }
213  
214 // remember name
215 o->name = name_arg;
216  
217 // check for existing provide with this name
218 if (find_provide(g, o->name)) {
219 ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
220 goto fail0;
221 }
222  
223 // insert to provides list
224 LinkedList1_Append(&g->provides_list, &o->provides_list_node);
225  
226 // init depends list
227 LinkedList1_Init(&o->depends_list);
228  
229 // set not dying
230 o->dying = 0;
231  
232 // signal up.
233 // This comes above the loop which follows, so that effects on related depend statements are
234 // computed before this process advances, avoiding problems like failed variable resolutions.
235 NCDModuleInst_Backend_Up(o->i);
236  
237 // update depends
238 for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
239 struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node);
240 depend_update(depend);
241 }
242  
243 return;
244  
245 fail0:
246 NCDModuleInst_Backend_DeadError(i);
247 }
248  
249 static void provide_free (struct provide *o)
250 {
251 struct global *g = ModuleGlobal(o->i);
252 ASSERT(LinkedList1_IsEmpty(&o->depends_list))
253  
254 // remove from provides list
255 LinkedList1_Remove(&g->provides_list, &o->provides_list_node);
256  
257 NCDModuleInst_Backend_Dead(o->i);
258 }
259  
260 static void provide_func_die (void *vo)
261 {
262 struct provide *o = vo;
263 ASSERT(!o->dying)
264  
265 // if we have no depends, die immediately
266 if (LinkedList1_IsEmpty(&o->depends_list)) {
267 provide_free(o);
268 return;
269 }
270  
271 // set dying
272 o->dying = 1;
273  
274 // start collapsing our depends
275 for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
276 struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node);
277 ASSERT(depend->provide == o)
278  
279 // update depend to make sure it is collapsing
280 depend_update(depend);
281 }
282 }
283  
284 static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
285 {
286 struct global *g = ModuleGlobal(i);
287 struct depend *o = vo;
288 o->i = i;
289  
290 // read arguments
291 NCDValRef names_arg;
292 if (!NCDVal_ListRead(params->args, 1, &names_arg)) {
293 ModuleLog(i, BLOG_ERROR, "wrong arity");
294 goto fail0;
295 }
296 if (!NCDVal_IsList(names_arg)) {
297 ModuleLog(o->i, BLOG_ERROR, "wrong type");
298 goto fail0;
299 }
300  
301 // remember names
302 o->names = names_arg;
303  
304 // insert to depends list
305 LinkedList1_Append(&g->depends_list, &o->depends_list_node);
306  
307 // set no provide
308 o->provide = NULL;
309  
310 // update
311 depend_update(o);
312 return;
313  
314 fail0:
315 NCDModuleInst_Backend_DeadError(i);
316 }
317  
318 static void depend_func_die (void *vo)
319 {
320 struct depend *o = vo;
321 struct global *g = ModuleGlobal(o->i);
322  
323 if (o->provide) {
324 // remove from provide's list
325 LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node);
326  
327 // if provide is dying and is empty, let it die
328 if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) {
329 provide_free(o->provide);
330 }
331 }
332  
333 // remove from depends list
334 LinkedList1_Remove(&g->depends_list, &o->depends_list_node);
335  
336 NCDModuleInst_Backend_Dead(o->i);
337 }
338  
339 static void depend_func_clean (void *vo)
340 {
341 struct depend *o = vo;
342  
343 if (!(o->provide && o->provide_collapsing)) {
344 return;
345 }
346  
347 // save provide
348 struct provide *provide = o->provide;
349  
350 // remove from provide's list
351 LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node);
352  
353 // set no provide
354 o->provide = NULL;
355  
356 // update
357 depend_update(o);
358  
359 // if provide is dying and is empty, let it die
360 if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) {
361 provide_free(provide);
362 }
363 }
364  
365 static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
366 {
367 struct depend *o = vo;
368  
369 if (!o->provide) {
370 return 0;
371 }
372  
373 return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
374 }
375  
376 static struct NCDModule modules[] = {
377 {
378 .type = "multiprovide",
379 .func_new2 = provide_func_new,
380 .func_die = provide_func_die,
381 .alloc_size = sizeof(struct provide)
382 }, {
383 .type = "multidepend",
384 .func_new2 = depend_func_new,
385 .func_die = depend_func_die,
386 .func_clean = depend_func_clean,
387 .func_getobj = depend_func_getobj,
388 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
389 .alloc_size = sizeof(struct depend)
390 }, {
391 .type = NULL
392 }
393 };
394  
395 const struct NCDModuleGroup ncdmodule_multidepend = {
396 .func_globalinit = func_globalinit,
397 .func_globalfree = func_globalfree,
398 .modules = modules
399 };