multidepend.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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. #include <stdlib.h>
  70. #include <string.h>
  71. #include <misc/offset.h>
  72. #include <misc/debug.h>
  73. #include <misc/balloc.h>
  74. #include <structure/LinkedList1.h>
  75. #include <ncd/module_common.h>
  76. #include <generated/blog_channel_ncd_multidepend.h>
  77. struct provide {
  78. NCDModuleInst *i;
  79. NCDValRef name;
  80. LinkedList1Node provides_list_node;
  81. LinkedList1 depends_list;
  82. int dying;
  83. };
  84. struct depend {
  85. NCDModuleInst *i;
  86. NCDValRef names;
  87. LinkedList1Node depends_list_node;
  88. struct provide *provide;
  89. LinkedList1Node provide_depends_list_node;
  90. int provide_collapsing;
  91. };
  92. struct global {
  93. LinkedList1 provides_list;
  94. LinkedList1 depends_list;
  95. };
  96. static struct provide * find_provide (struct global *g, NCDValRef name)
  97. {
  98. for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->provides_list); ln; ln = LinkedList1Node_Next(ln)) {
  99. struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node);
  100. if (NCDVal_Compare(provide->name, name) == 0) {
  101. return provide;
  102. }
  103. }
  104. return NULL;
  105. }
  106. static struct provide * depend_find_best_provide (struct depend *o)
  107. {
  108. struct global *g = ModuleGlobal(o->i);
  109. size_t count = NCDVal_ListCount(o->names);
  110. for (size_t j = 0; j < count; j++) {
  111. NCDValRef name = NCDVal_ListGet(o->names, j);
  112. struct provide *provide = find_provide(g, name);
  113. if (provide && !provide->dying) {
  114. return provide;
  115. }
  116. }
  117. return NULL;
  118. }
  119. static void depend_update (struct depend *o)
  120. {
  121. // if we're collapsing, do nothing
  122. if (o->provide && o->provide_collapsing) {
  123. return;
  124. }
  125. // find best provide
  126. struct provide *best_provide = depend_find_best_provide(o);
  127. ASSERT(!best_provide || !best_provide->dying)
  128. // has anything changed?
  129. if (best_provide == o->provide) {
  130. return;
  131. }
  132. if (o->provide) {
  133. // set collapsing
  134. o->provide_collapsing = 1;
  135. // signal down
  136. NCDModuleInst_Backend_Down(o->i);
  137. } else {
  138. // insert to provide's list
  139. LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node);
  140. // set not collapsing
  141. o->provide_collapsing = 0;
  142. // set provide
  143. o->provide = best_provide;
  144. // signal up
  145. NCDModuleInst_Backend_Up(o->i);
  146. }
  147. }
  148. static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params)
  149. {
  150. // allocate global state structure
  151. struct global *g = BAlloc(sizeof(*g));
  152. if (!g) {
  153. BLog(BLOG_ERROR, "BAlloc failed");
  154. return 0;
  155. }
  156. // set group state pointer
  157. group->group_state = g;
  158. // init provides list
  159. LinkedList1_Init(&g->provides_list);
  160. // init depends list
  161. LinkedList1_Init(&g->depends_list);
  162. return 1;
  163. }
  164. static void func_globalfree (struct NCDInterpModuleGroup *group)
  165. {
  166. struct global *g = group->group_state;
  167. ASSERT(LinkedList1_IsEmpty(&g->depends_list))
  168. ASSERT(LinkedList1_IsEmpty(&g->provides_list))
  169. // free global state structure
  170. BFree(g);
  171. }
  172. static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  173. {
  174. struct global *g = ModuleGlobal(i);
  175. struct provide *o = vo;
  176. o->i = i;
  177. // read arguments
  178. NCDValRef name_arg;
  179. if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
  180. ModuleLog(i, BLOG_ERROR, "wrong arity");
  181. goto fail0;
  182. }
  183. // remember name
  184. o->name = name_arg;
  185. // check for existing provide with this name
  186. if (find_provide(g, o->name)) {
  187. ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
  188. goto fail0;
  189. }
  190. // insert to provides list
  191. LinkedList1_Append(&g->provides_list, &o->provides_list_node);
  192. // init depends list
  193. LinkedList1_Init(&o->depends_list);
  194. // set not dying
  195. o->dying = 0;
  196. // signal up.
  197. // This comes above the loop which follows, so that effects on related depend statements are
  198. // computed before this process advances, avoiding problems like failed variable resolutions.
  199. NCDModuleInst_Backend_Up(o->i);
  200. // update depends
  201. for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  202. struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node);
  203. depend_update(depend);
  204. }
  205. return;
  206. fail0:
  207. NCDModuleInst_Backend_DeadError(i);
  208. }
  209. static void provide_free (struct provide *o)
  210. {
  211. struct global *g = ModuleGlobal(o->i);
  212. ASSERT(LinkedList1_IsEmpty(&o->depends_list))
  213. // remove from provides list
  214. LinkedList1_Remove(&g->provides_list, &o->provides_list_node);
  215. NCDModuleInst_Backend_Dead(o->i);
  216. }
  217. static void provide_func_die (void *vo)
  218. {
  219. struct provide *o = vo;
  220. ASSERT(!o->dying)
  221. // if we have no depends, die immediately
  222. if (LinkedList1_IsEmpty(&o->depends_list)) {
  223. provide_free(o);
  224. return;
  225. }
  226. // set dying
  227. o->dying = 1;
  228. // start collapsing our depends
  229. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  230. struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node);
  231. ASSERT(depend->provide == o)
  232. // update depend to make sure it is collapsing
  233. depend_update(depend);
  234. }
  235. }
  236. static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  237. {
  238. struct global *g = ModuleGlobal(i);
  239. struct depend *o = vo;
  240. o->i = i;
  241. // read arguments
  242. NCDValRef names_arg;
  243. if (!NCDVal_ListRead(params->args, 1, &names_arg)) {
  244. ModuleLog(i, BLOG_ERROR, "wrong arity");
  245. goto fail0;
  246. }
  247. if (!NCDVal_IsList(names_arg)) {
  248. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  249. goto fail0;
  250. }
  251. // remember names
  252. o->names = names_arg;
  253. // insert to depends list
  254. LinkedList1_Append(&g->depends_list, &o->depends_list_node);
  255. // set no provide
  256. o->provide = NULL;
  257. // update
  258. depend_update(o);
  259. return;
  260. fail0:
  261. NCDModuleInst_Backend_DeadError(i);
  262. }
  263. static void depend_func_die (void *vo)
  264. {
  265. struct depend *o = vo;
  266. struct global *g = ModuleGlobal(o->i);
  267. if (o->provide) {
  268. // remove from provide's list
  269. LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node);
  270. // if provide is dying and is empty, let it die
  271. if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) {
  272. provide_free(o->provide);
  273. }
  274. }
  275. // remove from depends list
  276. LinkedList1_Remove(&g->depends_list, &o->depends_list_node);
  277. NCDModuleInst_Backend_Dead(o->i);
  278. }
  279. static void depend_func_clean (void *vo)
  280. {
  281. struct depend *o = vo;
  282. if (!(o->provide && o->provide_collapsing)) {
  283. return;
  284. }
  285. // save provide
  286. struct provide *provide = o->provide;
  287. // remove from provide's list
  288. LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node);
  289. // set no provide
  290. o->provide = NULL;
  291. // update
  292. depend_update(o);
  293. // if provide is dying and is empty, let it die
  294. if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) {
  295. provide_free(provide);
  296. }
  297. }
  298. static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
  299. {
  300. struct depend *o = vo;
  301. if (!o->provide) {
  302. return 0;
  303. }
  304. return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
  305. }
  306. static struct NCDModule modules[] = {
  307. {
  308. .type = "multiprovide",
  309. .func_new2 = provide_func_new,
  310. .func_die = provide_func_die,
  311. .alloc_size = sizeof(struct provide)
  312. }, {
  313. .type = "multidepend",
  314. .func_new2 = depend_func_new,
  315. .func_die = depend_func_die,
  316. .func_clean = depend_func_clean,
  317. .func_getobj = depend_func_getobj,
  318. .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
  319. .alloc_size = sizeof(struct depend)
  320. }, {
  321. .type = NULL
  322. }
  323. };
  324. const struct NCDModuleGroup ncdmodule_multidepend = {
  325. .func_globalinit = func_globalinit,
  326. .func_globalfree = func_globalfree,
  327. .modules = modules
  328. };