multidepend.c 11 KB

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