depend_scope.c 14 KB

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