BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file depend_scope.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 * Multiple-option dependencies module.
32 *
33 * Synopsis:
34 * depend_scope()
35 *
36 * Description:
37 * A scope for dependency names. Any dependency names used in provide() and depend()
38 * methods on this object are local to this object. Contrast to the multidepend module,
39 * which provides the same functionality as this module, but with a single global
40 * dependency name scope.
41 *
42 * Synopsis:
43 * depend_scope::provide(name)
44 *
45 * Arguments:
46 * name - provider identifier
47 *
48 * Description:
49 * Satisfies a dependency.
50 * If any depend()'s get immediately bound to this provide(),
51 * the side effects of those first happen, and only then can the process of this
52 * provide() continue.
53 * When provide() is requested to deinitialize, if there are any depend()'s bound,
54 * provide() will not finish deinitializing until all the processes containing the
55 * bound depend()'s have backtracked to the point of the corresponding depend().
56 * More specifically, when backtracking has finished for the last bound depend(),
57 * first the immediate effects of the provide() finshing deinitialization will happen,
58 * and only then will the depend() attempt to rebind. (If the converse was true, the
59 * depend() could rebind, but when deinitialization of the provide()'s process
60 * continues, lose this binding. See ncd/tests/depend_scope.ncd .)
61 *
62 * Synopsis:
63 * depend_scope::depend(list names)
64 *
65 * Arguments:
66 * names - list of provider identifiers. Names more to the beginning are considered
67 * more desirable.
68 *
69 * Description:
70 * Binds to the provide() providing one of the specified dependency names which is most
71 * desirable. If there is no provide() providing any of the given dependency names,
72 * waits and binds when one becomes available.
73 * If the depend() is bound to a provide(), and the bound provide() is requested to
74 * deinitize, or a more desirable provide() becomes available, the depend() statement
75 * will go down (triggering backtracking), wait for backtracking to finish, and then
76 * try to bind to a provide() again, as if it was just initialized.
77 * When depend() is requested to deinitialize, it deinitializes immediately.
78 *
79 * Attributes:
80 * Exposes objects as seen from the corresponding provide.
81 */
82  
83 #include <stdlib.h>
84 #include <string.h>
85  
86 #include <misc/offset.h>
87 #include <misc/debug.h>
88 #include <misc/balloc.h>
89 #include <misc/BRefTarget.h>
90 #include <structure/LinkedList1.h>
91  
92 #include <ncd/module_common.h>
93  
94 #include <generated/blog_channel_ncd_depend_scope.h>
95  
96 struct scope {
97 BRefTarget ref_target;
98 LinkedList1 provides_list;
99 LinkedList1 depends_list;
100 };
101  
102 struct scope_instance {
103 NCDModuleInst *i;
104 struct scope *scope;
105 };
106  
107 struct provide {
108 NCDModuleInst *i;
109 struct scope *scope;
110 NCDValRef name;
111 LinkedList1Node provides_list_node;
112 LinkedList1 depends_list;
113 int dying;
114 };
115  
116 struct depend {
117 NCDModuleInst *i;
118 struct scope *scope;
119 NCDValRef names;
120 LinkedList1Node depends_list_node;
121 struct provide *provide;
122 LinkedList1Node provide_depends_list_node;
123 int provide_collapsing;
124 };
125  
126 static struct provide * find_provide (struct scope *o, NCDValRef name)
127 {
128 for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->provides_list); ln; ln = LinkedList1Node_Next(ln)) {
129 struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node);
130 ASSERT(provide->scope == o)
131 if (NCDVal_Compare(provide->name, name) == 0) {
132 return provide;
133 }
134 }
135  
136 return NULL;
137 }
138  
139 static struct provide * depend_find_best_provide (struct depend *o)
140 {
141 size_t count = NCDVal_ListCount(o->names);
142  
143 for (size_t j = 0; j < count; j++) {
144 NCDValRef name = NCDVal_ListGet(o->names, j);
145 struct provide *provide = find_provide(o->scope, name);
146 if (provide && !provide->dying) {
147 return provide;
148 }
149 }
150  
151 return NULL;
152 }
153  
154 static void depend_update (struct depend *o)
155 {
156 // if we're collapsing, do nothing
157 if (o->provide && o->provide_collapsing) {
158 return;
159 }
160  
161 // find best provide
162 struct provide *best_provide = depend_find_best_provide(o);
163 ASSERT(!best_provide || !best_provide->dying)
164  
165 // has anything changed?
166 if (best_provide == o->provide) {
167 return;
168 }
169  
170 if (o->provide) {
171 // set collapsing
172 o->provide_collapsing = 1;
173  
174 // signal down
175 NCDModuleInst_Backend_Down(o->i);
176 } else {
177 // insert to provide's list
178 LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node);
179  
180 // set not collapsing
181 o->provide_collapsing = 0;
182  
183 // set provide
184 o->provide = best_provide;
185  
186 // signal up
187 NCDModuleInst_Backend_Up(o->i);
188 }
189 }
190  
191 static void scope_ref_target_func_release (BRefTarget *ref_target)
192 {
193 struct scope *o = UPPER_OBJECT(ref_target, struct scope, ref_target);
194 ASSERT(LinkedList1_IsEmpty(&o->provides_list))
195 ASSERT(LinkedList1_IsEmpty(&o->depends_list))
196  
197 BFree(o);
198 }
199  
200 static void scope_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
201 {
202 struct scope_instance *o = vo;
203 o->i = i;
204  
205 // pass scope instance pointer to methods not NCDModuleInst pointer
206 NCDModuleInst_Backend_PassMemToMethods(i);
207  
208 // read arguments
209 if (!NCDVal_ListRead(params->args, 0)) {
210 ModuleLog(i, BLOG_ERROR, "wrong arity");
211 goto fail0;
212 }
213  
214 // allocate scope
215 o->scope = BAlloc(sizeof(*o->scope));
216 if (!o->scope) {
217 ModuleLog(i, BLOG_ERROR, "BAlloc failed");
218 goto fail0;
219 }
220  
221 // init reference target
222 BRefTarget_Init(&o->scope->ref_target, scope_ref_target_func_release);
223  
224 // init provide and depend lists
225 LinkedList1_Init(&o->scope->provides_list);
226 LinkedList1_Init(&o->scope->depends_list);
227  
228 // go up
229 NCDModuleInst_Backend_Up(i);
230 return;
231  
232 fail0:
233 NCDModuleInst_Backend_DeadError(i);
234 }
235  
236 static void scope_func_die (void *vo)
237 {
238 struct scope_instance *o = vo;
239  
240 // release scope reference
241 BRefTarget_Deref(&o->scope->ref_target);
242  
243 NCDModuleInst_Backend_Dead(o->i);
244 }
245  
246 static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
247 {
248 struct provide *o = vo;
249 o->i = i;
250 o->scope = ((struct scope_instance *)params->method_user)->scope;
251  
252 // read arguments
253 NCDValRef name_arg;
254 if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
255 ModuleLog(i, BLOG_ERROR, "wrong arity");
256 goto fail0;
257 }
258  
259 // remember name
260 o->name = name_arg;
261  
262 // check for existing provide with this name
263 if (find_provide(o->scope, o->name)) {
264 ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
265 goto fail0;
266 }
267  
268 // grab scope reference
269 if (!BRefTarget_Ref(&o->scope->ref_target)) {
270 ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed");
271 goto fail0;
272 }
273  
274 // insert to provides list
275 LinkedList1_Append(&o->scope->provides_list, &o->provides_list_node);
276  
277 // init depends list
278 LinkedList1_Init(&o->depends_list);
279  
280 // set not dying
281 o->dying = 0;
282  
283 // signal up.
284 // This comes above the loop which follows, so that effects on related depend statements are
285 // computed before this process advances, avoiding problems like failed variable resolutions.
286 NCDModuleInst_Backend_Up(o->i);
287  
288 // update depends
289 for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->scope->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
290 struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node);
291 depend_update(depend);
292 }
293  
294 return;
295  
296 fail0:
297 NCDModuleInst_Backend_DeadError(i);
298 }
299  
300 static void provide_free (struct provide *o)
301 {
302 ASSERT(LinkedList1_IsEmpty(&o->depends_list))
303  
304 // remove from provides list
305 LinkedList1_Remove(&o->scope->provides_list, &o->provides_list_node);
306  
307 // release scope reference
308 BRefTarget_Deref(&o->scope->ref_target);
309  
310 NCDModuleInst_Backend_Dead(o->i);
311 }
312  
313 static void provide_func_die (void *vo)
314 {
315 struct provide *o = vo;
316 ASSERT(!o->dying)
317  
318 // if we have no depends, die immediately
319 if (LinkedList1_IsEmpty(&o->depends_list)) {
320 provide_free(o);
321 return;
322 }
323  
324 // set dying
325 o->dying = 1;
326  
327 // start collapsing our depends
328 for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
329 struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node);
330 ASSERT(depend->provide == o)
331  
332 // update depend to make sure it is collapsing
333 depend_update(depend);
334 }
335 }
336  
337 static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
338 {
339 struct depend *o = vo;
340 o->i = i;
341 o->scope = ((struct scope_instance *)params->method_user)->scope;
342  
343 // read arguments
344 NCDValRef names_arg;
345 if (!NCDVal_ListRead(params->args, 1, &names_arg)) {
346 ModuleLog(i, BLOG_ERROR, "wrong arity");
347 goto fail0;
348 }
349 if (!NCDVal_IsList(names_arg)) {
350 ModuleLog(o->i, BLOG_ERROR, "wrong type");
351 goto fail0;
352 }
353  
354 // remember names
355 o->names = names_arg;
356  
357 // grab scope reference
358 if (!BRefTarget_Ref(&o->scope->ref_target)) {
359 ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed");
360 goto fail0;
361 }
362  
363 // insert to depends list
364 LinkedList1_Append(&o->scope->depends_list, &o->depends_list_node);
365  
366 // set no provide
367 o->provide = NULL;
368  
369 // update
370 depend_update(o);
371 return;
372  
373 fail0:
374 NCDModuleInst_Backend_DeadError(i);
375 }
376  
377 static void depend_func_die (void *vo)
378 {
379 struct depend *o = vo;
380  
381 if (o->provide) {
382 // remove from provide's list
383 LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node);
384  
385 // if provide is dying and is empty, let it die
386 if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) {
387 provide_free(o->provide);
388 }
389 }
390  
391 // remove from depends list
392 LinkedList1_Remove(&o->scope->depends_list, &o->depends_list_node);
393  
394 // release scope reference
395 BRefTarget_Deref(&o->scope->ref_target);
396  
397 NCDModuleInst_Backend_Dead(o->i);
398 }
399  
400 static void depend_func_clean (void *vo)
401 {
402 struct depend *o = vo;
403  
404 if (!(o->provide && o->provide_collapsing)) {
405 return;
406 }
407  
408 // save provide
409 struct provide *provide = o->provide;
410  
411 // remove from provide's list
412 LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node);
413  
414 // set no provide
415 o->provide = NULL;
416  
417 // update
418 depend_update(o);
419  
420 // if provide is dying and is empty, let it die.
421 // This comes after depend_update so that the side effects of the
422 // provide dying have priority over rebinding the depend.
423 if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) {
424 provide_free(provide);
425 }
426 }
427  
428 static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
429 {
430 struct depend *o = vo;
431  
432 if (!o->provide) {
433 return 0;
434 }
435  
436 return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
437 }
438  
439 static struct NCDModule modules[] = {
440 {
441 .type = "depend_scope",
442 .func_new2 = scope_func_new,
443 .func_die = scope_func_die,
444 .alloc_size = sizeof(struct scope_instance)
445 }, {
446 .type = "depend_scope::provide",
447 .func_new2 = provide_func_new,
448 .func_die = provide_func_die,
449 .alloc_size = sizeof(struct provide)
450 }, {
451 .type = "depend_scope::depend",
452 .func_new2 = depend_func_new,
453 .func_die = depend_func_die,
454 .func_clean = depend_func_clean,
455 .func_getobj = depend_func_getobj,
456 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
457 .alloc_size = sizeof(struct depend)
458 }, {
459 .type = NULL
460 }
461 };
462  
463 const struct NCDModuleGroup ncdmodule_depend_scope = {
464 .modules = modules
465 };