depend_scope.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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/NCDRefTarget.h>
  88. #include <structure/LinkedList1.h>
  89. #include <ncd/NCDModule.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_DeadError(i);
  204. }
  205. static void scope_func_die (void *vo)
  206. {
  207. struct scope_instance *o = vo;
  208. // release scope reference
  209. NCDRefTarget_Deref(&o->scope->ref_target);
  210. NCDModuleInst_Backend_Dead(o->i);
  211. }
  212. static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  213. {
  214. struct provide *o = vo;
  215. o->i = i;
  216. o->scope = ((struct scope_instance *)params->method_user)->scope;
  217. // read arguments
  218. NCDValRef name_arg;
  219. if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
  220. ModuleLog(i, BLOG_ERROR, "wrong arity");
  221. goto fail0;
  222. }
  223. // remember name
  224. o->name = name_arg;
  225. // check for existing provide with this name
  226. if (find_provide(o->scope, o->name)) {
  227. ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists");
  228. goto fail0;
  229. }
  230. // grab scope reference
  231. if (!NCDRefTarget_Ref(&o->scope->ref_target)) {
  232. ModuleLog(o->i, BLOG_ERROR, "NCDRefTarget_Ref failed");
  233. goto fail0;
  234. }
  235. // insert to provides list
  236. LinkedList1_Append(&o->scope->provides_list, &o->provides_list_node);
  237. // init depends list
  238. LinkedList1_Init(&o->depends_list);
  239. // set not dying
  240. o->dying = 0;
  241. // signal up.
  242. // This comes above the loop which follows, so that effects on related depend statements are
  243. // computed before this process advances, avoiding problems like failed variable resolutions.
  244. NCDModuleInst_Backend_Up(o->i);
  245. // update depends
  246. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->scope->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  247. struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node);
  248. depend_update(depend);
  249. }
  250. return;
  251. fail0:
  252. NCDModuleInst_Backend_DeadError(i);
  253. }
  254. static void provide_free (struct provide *o)
  255. {
  256. ASSERT(LinkedList1_IsEmpty(&o->depends_list))
  257. // remove from provides list
  258. LinkedList1_Remove(&o->scope->provides_list, &o->provides_list_node);
  259. // release scope reference
  260. NCDRefTarget_Deref(&o->scope->ref_target);
  261. NCDModuleInst_Backend_Dead(o->i);
  262. }
  263. static void provide_func_die (void *vo)
  264. {
  265. struct provide *o = vo;
  266. ASSERT(!o->dying)
  267. // if we have no depends, die immediately
  268. if (LinkedList1_IsEmpty(&o->depends_list)) {
  269. provide_free(o);
  270. return;
  271. }
  272. // set dying
  273. o->dying = 1;
  274. // start collapsing our depends
  275. for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) {
  276. struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node);
  277. ASSERT(depend->provide == o)
  278. // update depend to make sure it is collapsing
  279. depend_update(depend);
  280. }
  281. }
  282. static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  283. {
  284. struct depend *o = vo;
  285. o->i = i;
  286. o->scope = ((struct scope_instance *)params->method_user)->scope;
  287. // read arguments
  288. NCDValRef names_arg;
  289. if (!NCDVal_ListRead(params->args, 1, &names_arg)) {
  290. ModuleLog(i, BLOG_ERROR, "wrong arity");
  291. goto fail0;
  292. }
  293. if (!NCDVal_IsList(names_arg)) {
  294. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  295. goto fail0;
  296. }
  297. // remember names
  298. o->names = names_arg;
  299. // grab scope reference
  300. if (!NCDRefTarget_Ref(&o->scope->ref_target)) {
  301. ModuleLog(o->i, BLOG_ERROR, "NCDRefTarget_Ref failed");
  302. goto fail0;
  303. }
  304. // insert to depends list
  305. LinkedList1_Append(&o->scope->depends_list, &o->depends_list_node);
  306. // set no provide
  307. o->provide = NULL;
  308. // update
  309. depend_update(o);
  310. return;
  311. fail0:
  312. NCDModuleInst_Backend_DeadError(i);
  313. }
  314. static void depend_func_die (void *vo)
  315. {
  316. struct depend *o = vo;
  317. if (o->provide) {
  318. // remove from provide's list
  319. LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node);
  320. // if provide is dying and is empty, let it die
  321. if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) {
  322. provide_free(o->provide);
  323. }
  324. }
  325. // remove from depends list
  326. LinkedList1_Remove(&o->scope->depends_list, &o->depends_list_node);
  327. // release scope reference
  328. NCDRefTarget_Deref(&o->scope->ref_target);
  329. NCDModuleInst_Backend_Dead(o->i);
  330. }
  331. static void depend_func_clean (void *vo)
  332. {
  333. struct depend *o = vo;
  334. if (!(o->provide && o->provide_collapsing)) {
  335. return;
  336. }
  337. // save provide
  338. struct provide *provide = o->provide;
  339. // remove from provide's list
  340. LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node);
  341. // set no provide
  342. o->provide = NULL;
  343. // update
  344. depend_update(o);
  345. // if provide is dying and is empty, let it die.
  346. // This comes after depend_update so that the side effects of the
  347. // provide dying have priority over rebinding the depend.
  348. if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) {
  349. provide_free(provide);
  350. }
  351. }
  352. static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
  353. {
  354. struct depend *o = vo;
  355. if (!o->provide) {
  356. return 0;
  357. }
  358. return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
  359. }
  360. static struct NCDModule modules[] = {
  361. {
  362. .type = "depend_scope",
  363. .func_new2 = scope_func_new,
  364. .func_die = scope_func_die,
  365. .alloc_size = sizeof(struct scope_instance)
  366. }, {
  367. .type = "depend_scope::provide",
  368. .func_new2 = provide_func_new,
  369. .func_die = provide_func_die,
  370. .alloc_size = sizeof(struct provide)
  371. }, {
  372. .type = "depend_scope::depend",
  373. .func_new2 = depend_func_new,
  374. .func_die = depend_func_die,
  375. .func_clean = depend_func_clean,
  376. .func_getobj = depend_func_getobj,
  377. .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
  378. .alloc_size = sizeof(struct depend)
  379. }, {
  380. .type = NULL
  381. }
  382. };
  383. const struct NCDModuleGroup ncdmodule_depend_scope = {
  384. .modules = modules
  385. };