|
@@ -31,10 +31,10 @@
|
|
|
*
|
|
*
|
|
|
* Synopsis:
|
|
* Synopsis:
|
|
|
* try(string template_name, list args)
|
|
* try(string template_name, list args)
|
|
|
- * do(string template_name)
|
|
|
|
|
|
|
+ * do(string template_name[, string interrupt_template_name])
|
|
|
*
|
|
*
|
|
|
* Description:
|
|
* Description:
|
|
|
- * Does the following:
|
|
|
|
|
|
|
+ * The basic functionality is:
|
|
|
* 1. Starts a template process from the specified template and arguments.
|
|
* 1. Starts a template process from the specified template and arguments.
|
|
|
* 2. Waits for the process to initialize completely, or for a _try->assert()
|
|
* 2. Waits for the process to initialize completely, or for a _try->assert()
|
|
|
* assertion to fail or a _do->break() call.
|
|
* assertion to fail or a _do->break() call.
|
|
@@ -43,9 +43,21 @@
|
|
|
* managed to initialize, or an assertion failed.
|
|
* managed to initialize, or an assertion failed.
|
|
|
* If at any point during these steps termination of the try statement is
|
|
* If at any point during these steps termination of the try statement is
|
|
|
* requested, requests the process to terminate (if not already), and dies
|
|
* requested, requests the process to terminate (if not already), and dies
|
|
|
- * when it terminates. The differences between try() and do() are that do()
|
|
|
|
|
- * directly exposes the caller scope (try() does via _caller), and the
|
|
|
|
|
- * availability of assert/break.
|
|
|
|
|
|
|
+ * when it terminates.
|
|
|
|
|
+ *
|
|
|
|
|
+ * The differences between try() and do() are that do() directly exposes
|
|
|
|
|
+ * the caller scope (try() does via _caller), and the availability of
|
|
|
|
|
+ * assert/break.
|
|
|
|
|
+ *
|
|
|
|
|
+ * The two-argument version of do() is an extension, where in case of a
|
|
|
|
|
+ * statement termination request while the template process is not yet
|
|
|
|
|
+ * initialized completely, the interrupt_template_name template process is
|
|
|
|
|
+ * started, instead of sending a termination request to the template_name
|
|
|
|
|
+ * process. The interrupt_template_name process has access to the same
|
|
|
|
|
+ * scope as template_name. This means that it can itself call _do->break().
|
|
|
|
|
+ * Termination of the interrupt_template_name process is started as soon
|
|
|
|
|
+ * as it initializes completely, or when termination of the template_name
|
|
|
|
|
+ * process starts.
|
|
|
*
|
|
*
|
|
|
* Variables:
|
|
* Variables:
|
|
|
* string succeeded - "true" if the template process finished, "false" if assert
|
|
* string succeeded - "true" if the template process finished, "false" if assert
|
|
@@ -81,20 +93,21 @@
|
|
|
struct instance {
|
|
struct instance {
|
|
|
NCDModuleInst *i;
|
|
NCDModuleInst *i;
|
|
|
int is_do;
|
|
int is_do;
|
|
|
|
|
+ NCDValRef interrupt_template_name;
|
|
|
NCDModuleProcess process;
|
|
NCDModuleProcess process;
|
|
|
|
|
+ NCDModuleProcess interrupt_process;
|
|
|
int state;
|
|
int state;
|
|
|
|
|
+ int intstate;
|
|
|
int dying;
|
|
int dying;
|
|
|
int succeeded;
|
|
int succeeded;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED};
|
|
|
|
|
|
|
+enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED, STATE_WAITINT};
|
|
|
|
|
+enum {INTSTATE_NONE, INTSTATE_INIT, INTSTATE_DEINIT};
|
|
|
|
|
|
|
|
-static void process_handler_event (NCDModuleProcess *process, int event);
|
|
|
|
|
-static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
|
|
|
|
|
static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
|
|
static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
|
|
|
static void start_terminating (struct instance *o);
|
|
static void start_terminating (struct instance *o);
|
|
|
static void instance_free (struct instance *o);
|
|
static void instance_free (struct instance *o);
|
|
|
-static void instance_break (struct instance *o);
|
|
|
|
|
|
|
|
|
|
enum {STRING_TRY, STRING_TRY_TRY, STRING_DO, STRING_DO_DO};
|
|
enum {STRING_TRY, STRING_TRY_TRY, STRING_DO, STRING_DO_DO};
|
|
|
|
|
|
|
@@ -123,9 +136,13 @@ static void process_handler_event (NCDModuleProcess *process, int event)
|
|
|
// free process
|
|
// free process
|
|
|
NCDModuleProcess_Free(&o->process);
|
|
NCDModuleProcess_Free(&o->process);
|
|
|
|
|
|
|
|
- // die finally if requested
|
|
|
|
|
if (o->dying) {
|
|
if (o->dying) {
|
|
|
- instance_free(o);
|
|
|
|
|
|
|
+ // We want to die but not without interrupt_process still running.
|
|
|
|
|
+ if (o->intstate == INTSTATE_NONE) {
|
|
|
|
|
+ instance_free(o);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ o->state = STATE_WAITINT;
|
|
|
|
|
+ }
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -138,11 +155,44 @@ static void process_handler_event (NCDModuleProcess *process, int event)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
|
|
|
|
|
|
|
+static void interrupt_process_handler_event (NCDModuleProcess *process, int event)
|
|
|
{
|
|
{
|
|
|
- struct instance *o = UPPER_OBJECT(process, struct instance, process);
|
|
|
|
|
- ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
|
|
|
|
|
|
|
+ struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
|
|
|
|
|
+ ASSERT(o->dying)
|
|
|
|
|
+ ASSERT(o->intstate != INTSTATE_NONE)
|
|
|
|
|
|
|
|
|
|
+ switch (event) {
|
|
|
|
|
+ case NCDMODULEPROCESS_EVENT_UP: {
|
|
|
|
|
+ ASSERT(o->intstate == INTSTATE_INIT)
|
|
|
|
|
+
|
|
|
|
|
+ // Start terminating the interrupt_process.
|
|
|
|
|
+ NCDModuleProcess_Terminate(&o->interrupt_process);
|
|
|
|
|
+ o->intstate = INTSTATE_DEINIT;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ case NCDMODULEPROCESS_EVENT_DOWN: {
|
|
|
|
|
+ // Can't happen since we start terminating with it comes up.
|
|
|
|
|
+ ASSERT(0)
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ case NCDMODULEPROCESS_EVENT_TERMINATED: {
|
|
|
|
|
+ ASSERT(o->intstate == INTSTATE_DEINIT)
|
|
|
|
|
+
|
|
|
|
|
+ // free process
|
|
|
|
|
+ NCDModuleProcess_Free(&o->interrupt_process);
|
|
|
|
|
+ o->intstate = INTSTATE_NONE;
|
|
|
|
|
+
|
|
|
|
|
+ // If the main process has terminated, free the instance now.
|
|
|
|
|
+ if (o->state == STATE_WAITINT) {
|
|
|
|
|
+ instance_free(o);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } break;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int common_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
|
|
|
|
|
+{
|
|
|
if (o->is_do) {
|
|
if (o->is_do) {
|
|
|
if (name == ModuleString(o->i, STRING_DO)) {
|
|
if (name == ModuleString(o->i, STRING_DO)) {
|
|
|
*out_object = NCDObject_Build(ModuleString(o->i, STRING_DO_DO), o, NCDObject_no_getvar, NCDObject_no_getobj);
|
|
*out_object = NCDObject_Build(ModuleString(o->i, STRING_DO_DO), o, NCDObject_no_getvar, NCDObject_no_getobj);
|
|
@@ -165,10 +215,21 @@ static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
|
|
|
|
|
+{
|
|
|
|
|
+ struct instance *o = UPPER_OBJECT(process, struct instance, process);
|
|
|
|
|
+ return common_getspecialobj(o, name, out_object);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int interrupt_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
|
|
|
|
|
+{
|
|
|
|
|
+ struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
|
|
|
|
|
+ return common_getspecialobj(o, name, out_object);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
|
|
static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
|
|
|
{
|
|
{
|
|
|
struct instance *o = NCDObject_DataPtr(obj);
|
|
struct instance *o = NCDObject_DataPtr(obj);
|
|
|
- ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
|
|
|
|
|
|
|
|
|
|
return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
|
|
return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
|
|
|
}
|
|
}
|
|
@@ -177,18 +238,23 @@ static void start_terminating (struct instance *o)
|
|
|
{
|
|
{
|
|
|
ASSERT(o->state == STATE_INIT)
|
|
ASSERT(o->state == STATE_INIT)
|
|
|
|
|
|
|
|
|
|
+ // request termination of the interrupt_process if possible
|
|
|
|
|
+ if (o->intstate == INTSTATE_INIT) {
|
|
|
|
|
+ NCDModuleProcess_Terminate(&o->interrupt_process);
|
|
|
|
|
+ o->intstate = INTSTATE_DEINIT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// request process termination
|
|
// request process termination
|
|
|
NCDModuleProcess_Terminate(&o->process);
|
|
NCDModuleProcess_Terminate(&o->process);
|
|
|
-
|
|
|
|
|
- // set state deinit
|
|
|
|
|
o->state = STATE_DEINIT;
|
|
o->state = STATE_DEINIT;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args)
|
|
|
|
|
|
|
+static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args, NCDValRef interrupt_template_name)
|
|
|
{
|
|
{
|
|
|
struct instance *o = vo;
|
|
struct instance *o = vo;
|
|
|
o->i = i;
|
|
o->i = i;
|
|
|
o->is_do = is_do;
|
|
o->is_do = is_do;
|
|
|
|
|
+ o->interrupt_template_name = interrupt_template_name;
|
|
|
|
|
|
|
|
// start process
|
|
// start process
|
|
|
if (!NCDModuleProcess_InitValue(&o->process, i, template_name, args, process_handler_event)) {
|
|
if (!NCDModuleProcess_InitValue(&o->process, i, template_name, args, process_handler_event)) {
|
|
@@ -201,6 +267,7 @@ static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleI
|
|
|
|
|
|
|
|
// set state init, not dying, assume succeeded
|
|
// set state init, not dying, assume succeeded
|
|
|
o->state = STATE_INIT;
|
|
o->state = STATE_INIT;
|
|
|
|
|
+ o->intstate = INTSTATE_NONE;
|
|
|
o->dying = 0;
|
|
o->dying = 0;
|
|
|
o->succeeded = 1;
|
|
o->succeeded = 1;
|
|
|
return;
|
|
return;
|
|
@@ -223,7 +290,7 @@ static void func_new_try (void *vo, NCDModuleInst *i, const struct NCDModuleInst
|
|
|
goto fail;
|
|
goto fail;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return func_new_common(vo, i, params, 0, template_name_arg, args_arg);
|
|
|
|
|
|
|
+ return func_new_common(vo, i, params, 0, template_name_arg, args_arg, NCDVal_NewInvalid());
|
|
|
|
|
|
|
|
fail:
|
|
fail:
|
|
|
NCDModuleInst_Backend_DeadError(i);
|
|
NCDModuleInst_Backend_DeadError(i);
|
|
@@ -233,30 +300,35 @@ static void func_new_do (void *vo, NCDModuleInst *i, const struct NCDModuleInst_
|
|
|
{
|
|
{
|
|
|
// check arguments
|
|
// check arguments
|
|
|
NCDValRef template_name_arg;
|
|
NCDValRef template_name_arg;
|
|
|
- if (!NCDVal_ListRead(params->args, 1, &template_name_arg)) {
|
|
|
|
|
|
|
+ NCDValRef interrupt_template_name_arg = NCDVal_NewInvalid();
|
|
|
|
|
+ if (!NCDVal_ListRead(params->args, 1, &template_name_arg) &&
|
|
|
|
|
+ !NCDVal_ListRead(params->args, 2, &template_name_arg, &interrupt_template_name_arg)
|
|
|
|
|
+ ) {
|
|
|
ModuleLog(i, BLOG_ERROR, "wrong arity");
|
|
ModuleLog(i, BLOG_ERROR, "wrong arity");
|
|
|
goto fail;
|
|
goto fail;
|
|
|
}
|
|
}
|
|
|
- if (!NCDVal_IsString(template_name_arg)) {
|
|
|
|
|
|
|
+ if (!NCDVal_IsString(template_name_arg) ||
|
|
|
|
|
+ (!NCDVal_IsInvalid(interrupt_template_name_arg) && !NCDVal_IsString(interrupt_template_name_arg))
|
|
|
|
|
+ ) {
|
|
|
ModuleLog(i, BLOG_ERROR, "wrong type");
|
|
ModuleLog(i, BLOG_ERROR, "wrong type");
|
|
|
goto fail;
|
|
goto fail;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid());
|
|
|
|
|
|
|
+ return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid(), interrupt_template_name_arg);
|
|
|
|
|
|
|
|
fail:
|
|
fail:
|
|
|
NCDModuleInst_Backend_DeadError(i);
|
|
NCDModuleInst_Backend_DeadError(i);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void instance_free (struct instance *o)
|
|
static void instance_free (struct instance *o)
|
|
|
-{
|
|
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(o->intstate == INTSTATE_NONE)
|
|
|
|
|
+
|
|
|
NCDModuleInst_Backend_Dead(o->i);
|
|
NCDModuleInst_Backend_Dead(o->i);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void instance_break (struct instance *o)
|
|
static void instance_break (struct instance *o)
|
|
|
{
|
|
{
|
|
|
- ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
|
|
|
|
|
-
|
|
|
|
|
// mark not succeeded
|
|
// mark not succeeded
|
|
|
o->succeeded = 0;
|
|
o->succeeded = 0;
|
|
|
|
|
|
|
@@ -270,6 +342,7 @@ static void func_die (void *vo)
|
|
|
{
|
|
{
|
|
|
struct instance *o = vo;
|
|
struct instance *o = vo;
|
|
|
ASSERT(!o->dying)
|
|
ASSERT(!o->dying)
|
|
|
|
|
+ ASSERT(o->intstate == INTSTATE_NONE)
|
|
|
|
|
|
|
|
// if we're finished, die immediately
|
|
// if we're finished, die immediately
|
|
|
if (o->state == STATE_FINISHED) {
|
|
if (o->state == STATE_FINISHED) {
|
|
@@ -280,10 +353,27 @@ static void func_die (void *vo)
|
|
|
// set dying
|
|
// set dying
|
|
|
o->dying = 1;
|
|
o->dying = 1;
|
|
|
|
|
|
|
|
- // start terminating if not already
|
|
|
|
|
- if (o->state == STATE_INIT) {
|
|
|
|
|
- start_terminating(o);
|
|
|
|
|
|
|
+ // if already terminating, nothing to do
|
|
|
|
|
+ if (o->state != STATE_INIT) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // if we don't have the interrupt-template, start terminating
|
|
|
|
|
+ if (NCDVal_IsInvalid(o->interrupt_template_name)) {
|
|
|
|
|
+ goto terminate;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // start process
|
|
|
|
|
+ if (!NCDModuleProcess_InitValue(&o->interrupt_process, o->i, o->interrupt_template_name, NCDVal_NewInvalid(), interrupt_process_handler_event)) {
|
|
|
|
|
+ ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
|
|
|
|
|
+ goto terminate;
|
|
|
|
|
+ }
|
|
|
|
|
+ NCDModuleProcess_SetSpecialFuncs(&o->interrupt_process, interrupt_process_func_getspecialobj);
|
|
|
|
|
+ o->intstate = INTSTATE_INIT;
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+terminate:
|
|
|
|
|
+ start_terminating(o);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
|
|
static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
|