depend_scope.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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. #include <stdlib.h>
  83. #include <string.h>
  84. #include <misc/offset.h>
  85. #include <misc/debug.h>
  86. #include <misc/balloc.h>
  87. #include <misc/BRefTarget.h>
  88. #include <structure/LinkedList1.h>
  89. #include <ncd/module_common.h>
  90. #include <generated/blog_channel_ncd_depend_scope.h>
  91. struct scope {
  92. BRefTarget ref_target;
  93. LinkedList1 provides_list;
  94. LinkedList1 depends_list;
  95. };
  96. struct scope_instance {
  97. NCDModuleInst *i;
  98. struct scope *scope;
  99. };
  100. struct provide {
  101. NCDModuleInst *i;
  102. struct scope *scope;
  103. NCDValRef name;
  104. LinkedList1Node provides_list_node;
  105. LinkedList1 depends_list;
  106. int dying;
  107. };
  108. struct depend {
  109. NCDModuleInst *i;
  110. struct scope *scope;
  111. NCDValRef names;
  112. LinkedList1Node depends_list_node;
  113. struct provide *provide;
  114. LinkedList1Node provide_depends_list_node;
  115. int provide_collapsing;
  116. };
  117. static struct provide * find_provide (struct scope *o, NCDValRef name)
  118. {
  119. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->provides_list); ln; ln = LinkedList1Node_Next(ln)) {
  120. struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node);
  121. ASSERT(provide->scope == o)
  122. if (NCDVal_Compare(provide->name, name) == 0) {
  123. return provide;
  124. }
  125. }
  126. return NULL;
  127. }
  128. static struct provide * depend_find_best_provide (struct depend *o)
  129. {
  130. size_t count = NCDVal_ListCount(o->names);
  131. for (size_t j = 0; j < count; j++) {
  132. NCDValRef name = NCDVal_ListGet(o->names, j);
  133. struct provide *provide = find_provide(o->scope, name);
  134. if (provide && !provide->dying) {
  135. return provide;
  136. }
  137. }
  138. return NULL;
  139. }
  140. static void depend_update (struct depend *o)
  141. {
  142. // if we're collapsing, do nothing
  143. if (o->provide && o->provide_collapsing) {
  144. return;
  145. }
  146. // find best provide
  147. struct provide *best_provide = depend_find_best_provide(o);
  148. ASSERT(!best_provide || !best_provide->dying)
  149. // has anything changed?
  150. if (best_provide == o->provide) {
  151. return;
  152. }
  153. if (o->provide) {
  154. // set collapsing
  155. o->provide_collapsing = 1;
  156. // signal down
  157. NCDModuleInst_Backend_Down(o->i);
  158. } else {
  159. // insert to provide's list
  160. LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node);
  161. // set not collapsing
  162. o->provide_collapsing = 0;
  163. // set provide
  164. o->provide = best_provide;
  165. // signal up
  166. NCDModuleInst_Backend_Up(o->i);
  167. }
  168. }
  169. static void scope_ref_target_func_release (BRefTarget *ref_target)
  170. {
  171. struct scope *o = UPPER_OBJECT(ref_target, struct scope, ref_target);
  172. ASSERT(LinkedList1_IsEmpty(&o->provides_list))
  173. ASSERT(LinkedList1_IsEmpty(&o->depends_list))
  174. BFree(o);
  175. }
  176. static void scope_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  177. {
  178. struct scope_instance *o = vo;
  179. o->i = i;
  180. // pass scope instance pointer to methods not NCDModuleInst pointer
  181. NCDModuleInst_Backend_PassMemToMethods(i);
  182. // read arguments
  183. if (!NCDVal_ListRead(params->args, 0)) {
  184. ModuleLog(i, BLOG_ERROR, "wrong arity");
  185. goto fail0;
  186. }
  187. // allocate scope
  188. o->scope = BAlloc(sizeof(*o->scope));
  189. if (!o->scope) {
  190. ModuleLog(i, BLOG_ERROR, "BAlloc failed");
  191. goto fail0;
  192. }
  193. // init reference target
  194. BRefTarget_Init(&o->scope->ref_target, scope_ref_target_func_release);
  195. // init provide and depend lists
  196. LinkedList1_Init(&o->scope->provides_list);
  197. LinkedList1_Init(&o->scope->depends_list);
  198. // go up
  199. NCDModuleInst_Backend_Up(i);
  200. return;
  201. fail0:
  202. NCDModuleInst_Backend_DeadError(i);
  203. }
  204. static void scope_func_die (void *vo)
  205. {
  206. struct scope_instance *o = vo;
  207. // release scope reference
  208. BRefTarget_Deref(&o->scope->ref_target);
  209. NCDModuleInst_Backend_Dead(o->i);
  210. }
  211. static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  212. {
  213. struct provide *o = vo;
  214. o->i = i;
  215. o->scope = ((struct scope_instance *)params->method_user)->scope;
  216. // read arguments
  217. NCDValRef name_arg;
  218. if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
  219. ModuleLog(i, BLOG_ERROR, "wrong arity");
  220. goto fail0;
  221. }
  222. // remember name
  223. o->name = name_arg;
  224. // check for existing provide with this name
  225. if (find_provide(o->scope, o->name)) {
  226. ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
  227. goto fail0;
  228. }
  229. // grab scope reference
  230. if (!BRefTarget_Ref(&o->scope->ref_target)) {
  231. ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed");
  232. goto fail0;
  233. }
  234. // insert to provides list
  235. LinkedList1_Append(&o->scope->provides_list, &o->provides_list_node);
  236. // init depends list
  237. LinkedList1_Init(&o->depends_list);
  238. // set not dying
  239. o->dying = 0;
  240. // signal up.
  241. // This comes above the loop which follows, so that effects on related depend statements are
  242. // computed before this process advances, avoiding problems like failed variable resolutions.
  243. NCDModuleInst_Backend_Up(o->i);
  244. // update depends
  245. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->scope->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  246. struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node);
  247. depend_update(depend);
  248. }
  249. return;
  250. fail0:
  251. NCDModuleInst_Backend_DeadError(i);
  252. }
  253. static void provide_free (struct provide *o)
  254. {
  255. ASSERT(LinkedList1_IsEmpty(&o->depends_list))
  256. // remove from provides list
  257. LinkedList1_Remove(&o->scope->provides_list, &o->provides_list_node);
  258. // release scope reference
  259. BRefTarget_Deref(&o->scope->ref_target);
  260. NCDModuleInst_Backend_Dead(o->i);
  261. }
  262. static void provide_func_die (void *vo)
  263. {
  264. struct provide *o = vo;
  265. ASSERT(!o->dying)
  266. // if we have no depends, die immediately
  267. if (LinkedList1_IsEmpty(&o->depends_list)) {
  268. provide_free(o);
  269. return;
  270. }
  271. // set dying
  272. o->dying = 1;
  273. // start collapsing our depends
  274. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  275. struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node);
  276. ASSERT(depend->provide == o)
  277. // update depend to make sure it is collapsing
  278. depend_update(depend);
  279. }
  280. }
  281. static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  282. {
  283. struct depend *o = vo;
  284. o->i = i;
  285. o->scope = ((struct scope_instance *)params->method_user)->scope;
  286. // read arguments
  287. NCDValRef names_arg;
  288. if (!NCDVal_ListRead(params->args, 1, &names_arg)) {
  289. ModuleLog(i, BLOG_ERROR, "wrong arity");
  290. goto fail0;
  291. }
  292. if (!NCDVal_IsList(names_arg)) {
  293. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  294. goto fail0;
  295. }
  296. // remember names
  297. o->names = names_arg;
  298. // grab scope reference
  299. if (!BRefTarget_Ref(&o->scope->ref_target)) {
  300. ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed");
  301. goto fail0;
  302. }
  303. // insert to depends list
  304. LinkedList1_Append(&o->scope->depends_list, &o->depends_list_node);
  305. // set no provide
  306. o->provide = NULL;
  307. // update
  308. depend_update(o);
  309. return;
  310. fail0:
  311. NCDModuleInst_Backend_DeadError(i);
  312. }
  313. static void depend_func_die (void *vo)
  314. {
  315. struct depend *o = vo;
  316. if (o->provide) {
  317. // remove from provide's list
  318. LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node);
  319. // if provide is dying and is empty, let it die
  320. if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) {
  321. provide_free(o->provide);
  322. }
  323. }
  324. // remove from depends list
  325. LinkedList1_Remove(&o->scope->depends_list, &o->depends_list_node);
  326. // release scope reference
  327. BRefTarget_Deref(&o->scope->ref_target);
  328. NCDModuleInst_Backend_Dead(o->i);
  329. }
  330. static void depend_func_clean (void *vo)
  331. {
  332. struct depend *o = vo;
  333. if (!(o->provide && o->provide_collapsing)) {
  334. return;
  335. }
  336. // save provide
  337. struct provide *provide = o->provide;
  338. // remove from provide's list
  339. LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node);
  340. // set no provide
  341. o->provide = NULL;
  342. // update
  343. depend_update(o);
  344. // if provide is dying and is empty, let it die.
  345. // This comes after depend_update so that the side effects of the
  346. // provide dying have priority over rebinding the depend.
  347. if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) {
  348. provide_free(provide);
  349. }
  350. }
  351. static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
  352. {
  353. struct depend *o = vo;
  354. if (!o->provide) {
  355. return 0;
  356. }
  357. return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
  358. }
  359. static struct NCDModule modules[] = {
  360. {
  361. .type = "depend_scope",
  362. .func_new2 = scope_func_new,
  363. .func_die = scope_func_die,
  364. .alloc_size = sizeof(struct scope_instance)
  365. }, {
  366. .type = "depend_scope::provide",
  367. .func_new2 = provide_func_new,
  368. .func_die = provide_func_die,
  369. .alloc_size = sizeof(struct provide)
  370. }, {
  371. .type = "depend_scope::depend",
  372. .func_new2 = depend_func_new,
  373. .func_die = depend_func_die,
  374. .func_clean = depend_func_clean,
  375. .func_getobj = depend_func_getobj,
  376. .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
  377. .alloc_size = sizeof(struct depend)
  378. }, {
  379. .type = NULL
  380. }
  381. };
  382. const struct NCDModuleGroup ncdmodule_depend_scope = {
  383. .modules = modules
  384. };