try.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /**
  2. * @file try.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. *
  32. * Synopsis:
  33. * try(string template_name, list args)
  34. * do(string template_name)
  35. *
  36. * Description:
  37. * Does the following:
  38. * 1. Starts a template process from the specified template and arguments.
  39. * 2. Waits for the process to initialize completely, or for a _try->assert()
  40. * assertion to fail or a _do->break() call.
  41. * 3. Initiates termination of the process and waits for it to terminate.
  42. * 4. Goes to up state. The "succeeded" variable reflects whether the process
  43. * managed to initialize, or an assertion failed.
  44. * If at any point during these steps termination of the try statement is
  45. * requested, requests the process to terminate (if not already), and dies
  46. * when it terminates. The differences between try() and do() are that do()
  47. * directly exposes the caller scope (try() does via _caller), and the
  48. * availability of assert/break.
  49. *
  50. * Variables:
  51. * string succeeded - "true" if the template process finished, "false" if assert
  52. * or break was called.
  53. *
  54. *
  55. * Synopsis:
  56. * try.try::assert(string cond)
  57. *
  58. * Description:
  59. * Call as _try->assert() from the template process of try(). If cond is
  60. * "true", does nothing. Else, initiates termination of the process (if not
  61. * already), and marks the try operation as not succeeded.
  62. *
  63. *
  64. * Synopsis:
  65. * do.do::break()
  66. *
  67. * Description:
  68. * Call as _do->break() from the template process of do() to initiate
  69. * premature termination, marking the do operation as not succeeded.
  70. */
  71. #include <stdlib.h>
  72. #include <string.h>
  73. #include <misc/offset.h>
  74. #include <ncd/module_common.h>
  75. #include <generated/blog_channel_ncd_try.h>
  76. struct instance {
  77. NCDModuleInst *i;
  78. int is_do;
  79. NCDModuleProcess process;
  80. int state;
  81. int dying;
  82. int succeeded;
  83. };
  84. enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED};
  85. static void process_handler_event (NCDModuleProcess *process, int event);
  86. static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
  87. static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
  88. static void start_terminating (struct instance *o);
  89. static void instance_free (struct instance *o);
  90. static void instance_break (struct instance *o);
  91. enum {STRING_TRY, STRING_TRY_TRY, STRING_DO, STRING_DO_DO};
  92. static const char *strings[] = {"_try", "try.try", "_do", "do.do"};
  93. static void process_handler_event (NCDModuleProcess *process, int event)
  94. {
  95. struct instance *o = UPPER_OBJECT(process, struct instance, process);
  96. switch (event) {
  97. case NCDMODULEPROCESS_EVENT_UP: {
  98. ASSERT(o->state == STATE_INIT)
  99. // start terminating
  100. start_terminating(o);
  101. } break;
  102. case NCDMODULEPROCESS_EVENT_DOWN: {
  103. // Can't happen since we start terminating with it comes up.
  104. ASSERT(0)
  105. } break;
  106. case NCDMODULEPROCESS_EVENT_TERMINATED: {
  107. ASSERT(o->state == STATE_DEINIT)
  108. // free process
  109. NCDModuleProcess_Free(&o->process);
  110. // die finally if requested
  111. if (o->dying) {
  112. instance_free(o);
  113. return;
  114. }
  115. // signal up
  116. NCDModuleInst_Backend_Up(o->i);
  117. // set state finished
  118. o->state = STATE_FINISHED;
  119. } break;
  120. }
  121. }
  122. static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
  123. {
  124. struct instance *o = UPPER_OBJECT(process, struct instance, process);
  125. ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
  126. if (o->is_do) {
  127. if (name == ModuleString(o->i, STRING_DO)) {
  128. *out_object = NCDObject_Build(ModuleString(o->i, STRING_DO_DO), o, NCDObject_no_getvar, NCDObject_no_getobj);
  129. return 1;
  130. }
  131. return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
  132. } else {
  133. if (name == NCD_STRING_CALLER) {
  134. *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj);
  135. return 1;
  136. }
  137. if (name == ModuleString(o->i, STRING_TRY)) {
  138. *out_object = NCDObject_Build(ModuleString(o->i, STRING_TRY_TRY), o, NCDObject_no_getvar, NCDObject_no_getobj);
  139. return 1;
  140. }
  141. return 0;
  142. }
  143. }
  144. static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
  145. {
  146. struct instance *o = NCDObject_DataPtr(obj);
  147. ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
  148. return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
  149. }
  150. static void start_terminating (struct instance *o)
  151. {
  152. ASSERT(o->state == STATE_INIT)
  153. // request process termination
  154. NCDModuleProcess_Terminate(&o->process);
  155. // set state deinit
  156. o->state = STATE_DEINIT;
  157. }
  158. static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args)
  159. {
  160. struct instance *o = vo;
  161. o->i = i;
  162. o->is_do = is_do;
  163. // start process
  164. if (!NCDModuleProcess_InitValue(&o->process, i, template_name, args, process_handler_event)) {
  165. ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
  166. goto fail0;
  167. }
  168. // set special object function
  169. NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj);
  170. // set state init, not dying, assume succeeded
  171. o->state = STATE_INIT;
  172. o->dying = 0;
  173. o->succeeded = 1;
  174. return;
  175. fail0:
  176. NCDModuleInst_Backend_DeadError(i);
  177. }
  178. static void func_new_try (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  179. {
  180. // check arguments
  181. NCDValRef template_name_arg;
  182. NCDValRef args_arg;
  183. if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) {
  184. ModuleLog(i, BLOG_ERROR, "wrong arity");
  185. goto fail;
  186. }
  187. if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) {
  188. ModuleLog(i, BLOG_ERROR, "wrong type");
  189. goto fail;
  190. }
  191. return func_new_common(vo, i, params, 0, template_name_arg, args_arg);
  192. fail:
  193. NCDModuleInst_Backend_DeadError(i);
  194. }
  195. static void func_new_do (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  196. {
  197. // check arguments
  198. NCDValRef template_name_arg;
  199. if (!NCDVal_ListRead(params->args, 1, &template_name_arg)) {
  200. ModuleLog(i, BLOG_ERROR, "wrong arity");
  201. goto fail;
  202. }
  203. if (!NCDVal_IsString(template_name_arg)) {
  204. ModuleLog(i, BLOG_ERROR, "wrong type");
  205. goto fail;
  206. }
  207. return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid());
  208. fail:
  209. NCDModuleInst_Backend_DeadError(i);
  210. }
  211. static void instance_free (struct instance *o)
  212. {
  213. NCDModuleInst_Backend_Dead(o->i);
  214. }
  215. static void instance_break (struct instance *o)
  216. {
  217. ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
  218. // mark not succeeded
  219. o->succeeded = 0;
  220. // start terminating if not already
  221. if (o->state == STATE_INIT) {
  222. start_terminating(o);
  223. }
  224. }
  225. static void func_die (void *vo)
  226. {
  227. struct instance *o = vo;
  228. ASSERT(!o->dying)
  229. // if we're finished, die immediately
  230. if (o->state == STATE_FINISHED) {
  231. instance_free(o);
  232. return;
  233. }
  234. // set dying
  235. o->dying = 1;
  236. // start terminating if not already
  237. if (o->state == STATE_INIT) {
  238. start_terminating(o);
  239. }
  240. }
  241. static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  242. {
  243. struct instance *o = vo;
  244. ASSERT(o->state == STATE_FINISHED)
  245. ASSERT(!o->dying)
  246. if (name == NCD_STRING_SUCCEEDED) {
  247. *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index);
  248. return 1;
  249. }
  250. return 0;
  251. }
  252. static void assert_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  253. {
  254. // check arguments
  255. NCDValRef cond_arg;
  256. if (!NCDVal_ListRead(params->args, 1, &cond_arg)) {
  257. ModuleLog(i, BLOG_ERROR, "wrong arity");
  258. goto fail1;
  259. }
  260. if (!NCDVal_IsString(cond_arg)) {
  261. ModuleLog(i, BLOG_ERROR, "wrong type");
  262. goto fail1;
  263. }
  264. // signal up
  265. NCDModuleInst_Backend_Up(i);
  266. // break if needed
  267. if (!NCDVal_StringEquals(cond_arg, "true")) {
  268. instance_break((struct instance *)params->method_user);
  269. }
  270. return;
  271. fail1:
  272. NCDModuleInst_Backend_DeadError(i);
  273. }
  274. static void break_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  275. {
  276. // check arguments
  277. if (!NCDVal_ListRead(params->args, 0)) {
  278. ModuleLog(i, BLOG_ERROR, "wrong arity");
  279. goto fail1;
  280. }
  281. // signal up
  282. NCDModuleInst_Backend_Up(i);
  283. // break
  284. instance_break((struct instance *)params->method_user);
  285. return;
  286. fail1:
  287. NCDModuleInst_Backend_DeadError(i);
  288. }
  289. static struct NCDModule modules[] = {
  290. {
  291. .type = "try",
  292. .func_new2 = func_new_try,
  293. .func_die = func_die,
  294. .func_getvar2 = func_getvar2,
  295. .alloc_size = sizeof(struct instance)
  296. }, {
  297. .type = "do",
  298. .func_new2 = func_new_do,
  299. .func_die = func_die,
  300. .func_getvar2 = func_getvar2,
  301. .alloc_size = sizeof(struct instance)
  302. }, {
  303. .type = "try.try::assert",
  304. .func_new2 = assert_func_new
  305. }, {
  306. .type = "do.do::break",
  307. .func_new2 = break_func_new
  308. }, {
  309. .type = NULL
  310. }
  311. };
  312. const struct NCDModuleGroup ncdmodule_try = {
  313. .modules = modules,
  314. .strings = strings
  315. };