From: Kevin Day Date: Tue, 26 Jan 2021 03:24:13 +0000 (-0600) Subject: Progress: controller program and related. X-Git-Tag: 0.5.3~108 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=cac912069ab4ce99cbb8be6203a182d0a1162cbf;p=fll Progress: controller program and related. Add f_signal_send(). Cleanup some of the f_signal code. Add "return" mode option to fl_execute. This adds more thread related changes, much if this I am semi-experimenting. I will likely do a post review and try to clean it up and remove anything unnecessary. One thing in particular that I am trying is saving the child process PID for a foreground process. This can then be manually sent a termination signal on exit. The program should exit in certain validation modes. Cleanup f_environment logic in one function. --- diff --git a/level_0/f_environment/c/private-environment.c b/level_0/f_environment/c/private-environment.c index b328415..c102d37 100644 --- a/level_0/f_environment/c/private-environment.c +++ b/level_0/f_environment/c/private-environment.c @@ -15,10 +15,7 @@ extern "C" { const f_string_length_t size = strnlen(result, f_environment_max_length); - if (!size) { - value->used = 0; - } - else { + if (size) { if (value->used + size > f_environment_max_length) { return F_status_set_error(F_string_too_large); } @@ -33,6 +30,9 @@ extern "C" { memcpy(value->string + value->used, result, value->used + size); value->used = size; } + else { + value->used = 0; + } return F_none; } diff --git a/level_0/f_signal/c/signal.c b/level_0/f_signal/c/signal.c index 76609c6..d046c87 100644 --- a/level_0/f_signal/c/signal.c +++ b/level_0/f_signal/c/signal.c @@ -99,6 +99,21 @@ extern "C" { } #endif // _di_f_signal_read_ +#ifndef _di_f_signal_send_ + f_status_t f_signal_send(const int signal, const pid_t process_id) { + + if (kill(process_id, signal) < 0) { + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == ESRCH) return F_status_set_error(F_found_not); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_signal_send_ + #ifndef _di_f_signal_set_add_ f_status_t f_signal_set_add(const int signal, sigset_t *set) { #ifndef _di_level_0_parameter_checking_ @@ -106,9 +121,7 @@ extern "C" { #endif // _di_level_0_parameter_checking_ if (sigaddset(set, signal) < 0) { - if (errno == EINVAL) { - return F_status_set_error(F_parameter); - } + if (errno == EINVAL) return F_status_set_error(F_parameter); return F_status_set_error(F_failure); } @@ -124,9 +137,7 @@ extern "C" { #endif // _di_level_0_parameter_checking_ if (sigdelset(set, signal) < 0) { - if (errno == EINVAL) { - return F_status_set_error(F_parameter); - } + if (errno == EINVAL) return F_status_set_error(F_parameter); return F_status_set_error(F_failure); } @@ -142,9 +153,7 @@ extern "C" { #endif // _di_level_0_parameter_checking_ if (sigemptyset(set) < 0) { - if (errno == EINVAL) { - return F_status_set_error(F_parameter); - } + if (errno == EINVAL) return F_status_set_error(F_parameter); return F_status_set_error(F_failure); } diff --git a/level_0/f_signal/c/signal.h b/level_0/f_signal/c/signal.h index 625bbff..d3370ec 100644 --- a/level_0/f_signal/c/signal.h +++ b/level_0/f_signal/c/signal.h @@ -86,6 +86,7 @@ extern "C" { * F_none on success but no signal found. * F_data_not on success, but no descriptor was provided to read. * F_signal on success and signal found. + * * F_block (with error bit) if file descriptor is set to non-block and the read would result in a blocking operation. * F_buffer (with error bit) if the buffer is invalid. * F_descriptor (with error bit) if the signal descriptor is invalid. @@ -104,6 +105,29 @@ extern "C" { #endif // _di_f_signal_read_ /** + * Send a signal to a process. + * + * @param signal + * The signal to send + * @param process_id + * The process id (PID) that will receive the signal. + * This may also be a process group id. + * + * @return + * F_none on success but no signal found. + * + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if not allowed to send signals to the given process. + * F_found_not (with error bit) if the given process was not found. + * F_failure (with error bit) for any other error. + * + * @see kill() + */ +#ifndef _di_f_signal_send_ + extern f_status_t f_signal_send(const int signal, const pid_t process_id); +#endif // _di_f_signal_send_ + +/** * Add a signal to the given set of signals. * * @param signal @@ -113,6 +137,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * @@ -132,6 +157,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * @@ -149,6 +175,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * @@ -166,6 +193,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * @@ -192,6 +220,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * @@ -213,6 +242,7 @@ extern "C" { * * @return * F_none on success but no signal found. + * * F_found_not (with error bit) if the given PID was found. * F_parameter (with error bit) if a parameter is invalid. * F_resource_not (with error bit) if the max signals is reached. @@ -236,6 +266,7 @@ extern "C" { * @return * F_true if signal is found. * F_false if signal is not found. + * * F_parameter (with error bit) if a parameter is invalid. * F_failure (with error bit) for any other error. * diff --git a/level_1/fl_execute/c/execute-common.h b/level_1/fl_execute/c/execute-common.h index dbbacce..dbfb96c 100644 --- a/level_1/fl_execute/c/execute-common.h +++ b/level_1/fl_execute/c/execute-common.h @@ -23,6 +23,7 @@ extern "C" { * fl_execute_parameter_option_exit: used to desginate to exit after calling child otherwise child process will return. * fl_execute_parameter_option_path: used to designate that this is a path to a program (such as '/bin/bash'). * fl_execute_parameter_option_threadsafe: used to designate that threadsafe functions are to be used (such as: f_thread_signal_mask instead of f_signal_mask). + * fl_execute_parameter_option_return: used to designate that the parent process will immediately return instead of waiting for the child process to complete. * * If thread support is disabled in the library, then fl_execute_parameter_option_threadsafe will fallback to non-threadsafe. * @@ -35,6 +36,7 @@ extern "C" { #define fl_execute_parameter_option_exit 0x1 #define fl_execute_parameter_option_path 0x2 #define fl_execute_parameter_option_threadsafe 0x4 + #define fl_execute_parameter_option_return 0x8 typedef struct { uint8_t option; diff --git a/level_2/fll_execute/c/execute.h b/level_2/fll_execute/c/execute.h index a9006b6..7b9c300 100644 --- a/level_2/fll_execute/c/execute.h +++ b/level_2/fll_execute/c/execute.h @@ -368,6 +368,7 @@ extern "C" { * * @return * F_none on success. + * * F_failure (with error bit) on execution failure. * * @see execv() @@ -426,13 +427,17 @@ extern "C" { * A pointer to a string to pipe as standard input to the child process. * The parent will block until the standard input is fully read or the child process exits. * @param as - * (optional) This and most of its fields are optional and are disabled when set to 0. + * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * The code returned after finishing execution of program. + * (optional) The code returned after finishing execution of program. + * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). + * Set to NULL to not use. * * @return * F_none on success. - * F_child on success but this is the child thread. + * F_child on success and this is the child thread. + * F_parent on success and this is the parent thread (only happens when fl_execute_parameter_option_return is passed). + * * F_capability (with error bit) on failure to set capabilities in the child (only the child process returns this). * F_control_group (with error bit) on failure to set control group in the child (only the parent process returns this). * F_child (with error bit) on any failure without an explicit failure code (like F_group) before calling execute but this is the child thread. diff --git a/level_2/fll_execute/c/private-execute.c b/level_2/fll_execute/c/private-execute.c index 7b98241..3bf527d 100644 --- a/level_2/fll_execute/c/private-execute.c +++ b/level_2/fll_execute/c/private-execute.c @@ -309,6 +309,15 @@ extern "C" { } } + if (parameter && parameter->option & fl_execute_parameter_option_return) { + + if (result != 0) { + *result = id_process; + } + + return F_parent; + } + // have the parent wait for the child process to finish. waitpid(id_process, result, WUNTRACED | WCONTINUED); @@ -469,6 +478,15 @@ extern "C" { } } + if (parameter && parameter->option & fl_execute_parameter_option_return) { + + if (result != 0) { + *result = id_process; + } + + return F_parent; + } + // have the parent wait for the child process to finish. waitpid(id_process, result, WUNTRACED | WCONTINUED); diff --git a/level_2/fll_execute/c/private-execute.h b/level_2/fll_execute/c/private-execute.h index d24136c..1324303 100644 --- a/level_2/fll_execute/c/private-execute.h +++ b/level_2/fll_execute/c/private-execute.h @@ -106,6 +106,7 @@ extern "C" { * * @return * F_none on success. + * * F_capability (with error bit) on failure to set capabilities. * F_group (with error bit) on failure to set GID. * F_nice (with error bit) on failure to set process niceness. @@ -146,6 +147,7 @@ extern "C" { * * @return * F_none on success. + * * F_control_group (with error bit) on failure to set control group. * F_limit (with error bit) on failure to set a resource limit. * F_processor (with error bit) on failure to set processor (cpu) affinity. @@ -196,13 +198,17 @@ extern "C" { * A pointer to a string to pipe as standard input to the child process. * The parent will block until the standard input is fully read or the child process exits. * @param as - * (optional) This and most of its fields are optional and are disabled when set to 0. + * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * The code returned after finishing execution of program. + * (optional) The code returned after finishing execution of program. + * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). + * Set to NULL to not use. * * @return * F_none on success. - * F_child on success but this is the child thread. + * F_child on success and this is the child thread. + * F_parent on success and this is the parent thread (only happens when fl_execute_parameter_option_return is passed). + * * F_capability (with error bit) on failure to set capabilities in the child (only the child process returns this). * F_control_group (with error bit) on failure to set control group in the child (only the parent process returns this). * F_child (with error bit) on any failure without an explicit failure code (like F_group) before calling execute but this is the child thread. @@ -264,13 +270,17 @@ extern "C" { * A pointer to a string to pipe as standard input to the child process. * The parent will block until the standard input is fully read or the child process exits. * @param as - * (optional) This and most of its fields are optional and are disabled when set to 0. + * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * The code returned after finishing execution of program. + * (optional) The code returned after finishing execution of program. + * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). + * Set to NULL to not use. * * @return * F_none on success. - * F_child on success but this is the child thread. + * F_child on success and this is the child thread. + * F_parent on success and this is the parent thread (only happens when fl_execute_parameter_option_return is passed). + * * F_capability (with error bit) on failure to set capabilities in the child (only the child process returns this). * F_control_group (with error bit) on failure to set control group in the child (only the parent process returns this). * F_child (with error bit) on any failure without an explicit failure code (like F_group) before calling execute but this is the child thread. diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index 6085c7b..86bc79c 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -482,6 +482,7 @@ extern "C" { f_status_t status; f_thread_mutex_t lock; + f_thread_mutex_t wait; f_number_unsigned_t timeout_kill; f_number_unsigned_t timeout_start; @@ -523,6 +524,7 @@ extern "C" { { \ F_known_not, \ f_thread_mutex_t_initialize, \ + f_thread_mutex_t_initialize, \ 0, \ 0, \ 0, \ @@ -552,6 +554,7 @@ extern "C" { #define controller_macro_rule_t_delete_simple(rule) \ f_macro_thread_mutex_t_delete_simple(rule.lock) \ + f_macro_thread_mutex_t_delete_simple(rule.wait) \ f_macro_string_dynamic_t_delete_simple(rule.id) \ f_macro_string_dynamic_t_delete_simple(rule.name) \ f_macro_string_dynamic_t_delete_simple(rule.path) \ @@ -891,13 +894,14 @@ extern "C" { uint8_t state; uint8_t action; uint8_t options; + pid_t child; void *thread; f_array_lengths_t stack; controller_cache_action_t cache; } controller_asynchronous_t; - #define controller_asynchronous_t_initialize { f_thread_id_t_initialize, 0, 0, 0, 0, 0, f_array_lengths_t_initialize, controller_cache_action_t_initialize } + #define controller_asynchronous_t_initialize { f_thread_id_t_initialize, 0, 0, 0, 0, 0, 0, f_array_lengths_t_initialize, controller_cache_action_t_initialize } #define controller_macro_asynchronous_t_clear(asynchronous) \ f_macro_thread_id_t_clear(asynchronous.id) \ @@ -905,6 +909,7 @@ extern "C" { asynchronous.state = 0; \ asynchronous.action = 0; \ asynchronous.options = 0; \ + asynchronous.child = 0; \ asynchronous.thread = 0; \ f_macro_array_lengths_t_clear(asynchronous.stack) \ controller_macro_cache_action_t_clear(asynchronous.cache) @@ -916,6 +921,8 @@ extern "C" { #ifndef _di_controller_asynchronouss_t_ typedef struct { + bool enabled; + controller_asynchronous_t *array; f_array_length_t size; @@ -924,6 +931,7 @@ extern "C" { #define controller_asynchronouss_t_initialize \ { \ + F_true, \ 0, \ 0, \ 0, \ diff --git a/level_3/controller/c/private-controller.c b/level_3/controller/c/private-controller.c index 02ba77e..755de05 100644 --- a/level_3/controller/c/private-controller.c +++ b/level_3/controller/c/private-controller.c @@ -577,7 +577,6 @@ extern "C" { controller_entry_actions_t *actions = 0; const bool simulate = data->parameters[controller_parameter_test].result == f_console_result_found; - const bool validate = data->parameters[controller_parameter_validate].result == f_console_result_found; cache->ats.used = 0; cache->stack.used = 0; diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 2516ed3..d6caac2 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -613,7 +613,7 @@ extern "C" { action = &item->actions.array[j]; execute_set.parameter.data = 0; - execute_set.parameter.option = fl_execute_parameter_option_threadsafe; + execute_set.parameter.option = fl_execute_parameter_option_threadsafe & fl_execute_parameter_option_return; if (item->type == controller_rule_item_type_command) { @@ -767,6 +767,24 @@ extern "C" { status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, &result); } + if (status == F_parent) { + controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) thread->setting->rules.array[index].asynchronous; + + // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process. + asynchronous->child = result; + + // have the parent wait for the child process to finish. (@todo see comments above about forking into the background, this code block will need to change.) + waitpid(asynchronous->child, &result, WUNTRACED | WCONTINUED); + + // remove the pid now that waidpid() has returned. + asynchronous->child = 0; + + // this must explicitly check for 0 (as opposed to checking (!result)). + if (!WIFEXITED(result)) { + status = F_status_set_error(F_failure); + } + } + if (F_status_is_error(status)) { status = F_status_set_fine(status); @@ -858,6 +876,24 @@ extern "C" { status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, &result); } + if (status == F_parent) { + controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) thread->setting->rules.array[index].asynchronous; + + // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process. + asynchronous->child = result; + + // have the parent wait for the child process to finish. (@todo see comments above about forking into the background, this code block will need to change.) + waitpid(asynchronous->child, &result, WUNTRACED | WCONTINUED); + + // remove the pid now that waidpid() has returned. + asynchronous->child = 0; + + // this must explicitly check for 0 (as opposed to checking (!result)). + if (!WIFEXITED(result)) { + status = F_status_set_error(F_failure); + } + } + if (F_status_is_error(status)) { status = F_status_set_fine(status); @@ -1661,6 +1697,12 @@ extern "C" { f_thread_mutex_lock(&thread->mutex->asynchronous); + if (!thread->asynchronouss.enabled) { + f_thread_mutex_unlock(&thread->mutex->asynchronous); + + return F_signal; + } + f_status_t status = controller_asynchronouss_increase(&thread->asynchronouss); if (F_status_is_error(status)) { @@ -3917,29 +3959,11 @@ extern "C" { #ifndef _di_controller_rule_wait_all_ void controller_rule_wait_all(controller_thread_t *thread) { - f_array_length_t i = 0; - - for (; i < thread->asynchronouss.used; ++i) { + for (f_array_length_t i = 0; i < thread->asynchronouss.used; ++i) { if (!thread->asynchronouss.array[i].state) continue; - if (thread->asynchronouss.array[i].state != controller_asynchronous_state_joined) { - f_thread_join(thread->asynchronouss.array[i].id, 0); - } - - f_thread_mutex_lock(&thread->mutex->asynchronous); - - if (thread->asynchronouss.array[i].state) { - thread->asynchronouss.array[i].state = 0; - - controller_macro_cache_action_t_clear(thread->asynchronouss.array[i].cache); - } - - if (i == thread->asynchronouss.used - 1) { - thread->asynchronouss.used = 0; - } - - f_thread_mutex_unlock(&thread->mutex->asynchronous); + controller_rule_wait_for(i, thread); } // for } #endif // _di_controller_rule_wait_all_ @@ -3957,21 +3981,36 @@ extern "C" { return; } - controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) rule->asynchronous; + if (f_thread_mutex_lock_try(&rule->wait) == F_none) { + controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) rule->asynchronous; - if (asynchronous->state != controller_asynchronous_state_joined) { - f_thread_join(asynchronous->id, 0); - } + if (asynchronous->state == controller_asynchronous_state_done) { + f_thread_join(asynchronous->id, 0); + } - f_thread_mutex_lock(&thread->mutex->asynchronous); + if (thread->asynchronouss.enabled) { + f_thread_mutex_lock(&thread->mutex->asynchronous); + + if (asynchronous->state) { + if (asynchronous->state == controller_asynchronous_state_done) { + asynchronous->state = controller_asynchronous_state_joined; + } + + controller_macro_cache_action_t_clear(asynchronous->cache); + } - if (asynchronous->state) { - asynchronous->state = 0; + f_thread_mutex_unlock(&thread->mutex->asynchronous); + } - controller_macro_cache_action_t_clear(asynchronous->cache); + f_thread_mutex_unlock(&rule->wait); } + else { - f_thread_mutex_unlock(&thread->mutex->asynchronous); + // a wait lock is already in place, which will also be responsible for thread joining. + // this can therefore immediately unlock and return. + f_thread_mutex_lock(&rule->wait); + f_thread_mutex_unlock(&rule->wait); + } } #endif // _di_controller_rule_wait_for_ diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index dd6f98e..c852788 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -18,6 +18,10 @@ extern "C" { { controller_thread_t *thread_main = (controller_thread_t *) asynchronous->thread; + if (!thread_main->asynchronouss.enabled) { + return 0; + } + f_thread_mutex_lock(&thread_main->setting->rules.array[asynchronous->index].lock); thread.cache_main = thread_main->cache_main; @@ -30,6 +34,8 @@ extern "C" { controller_rule_process(asynchronous->index, asynchronous->action, asynchronous->options, &thread); + asynchronous->state = controller_asynchronous_state_done; + f_thread_mutex_unlock(&thread.setting->rules.array[asynchronous->index].lock); return 0; @@ -39,13 +45,21 @@ extern "C" { #ifndef _di_controller_thread_asynchronous_cancel_ void controller_thread_asynchronous_cancel(controller_thread_t *thread) { + thread->asynchronouss.enabled = F_false; + f_thread_mutex_lock(&thread->mutex->asynchronous); - for (f_array_length_t i = 0; i < thread->asynchronouss.used; ++i) { + f_array_length_t i = 0; + + for (; i < thread->asynchronouss.used; ++i) { if (!thread->asynchronouss.array[i].state) continue; - f_thread_cancel(thread->asynchronouss.array[i].id); + if (thread->asynchronouss.array[i].child > 0) { + f_signal_send(F_signal_termination, thread->asynchronouss.array[i].child); + } + + // @todo perhaps a timed join here where if it takes to long, try sending a kill signal to the child process. f_thread_join(thread->asynchronouss.array[i].id, 0); thread->asynchronouss.array[i].state = 0; @@ -55,6 +69,14 @@ extern "C" { thread->asynchronouss.used = 0; + for (i = 0; i < thread->setting->rules.used; ++i) { + f_thread_mutex_unlock(&thread->setting->rules.array[i].lock); + f_thread_mutex_unlock(&thread->setting->rules.array[i].wait); + } // for + + f_thread_mutex_unlock(&thread->mutex->print); + f_thread_mutex_unlock(&thread->mutex->cache); + f_thread_mutex_unlock(&thread->mutex->rule); f_thread_mutex_unlock(&thread->mutex->asynchronous); } #endif // _di_controller_thread_asynchronous_cancel_ @@ -63,14 +85,11 @@ extern "C" { void * controller_thread_cache(void *arguments) { controller_thread_t *thread = (controller_thread_t *) arguments; + controller_rule_t *rule = 0; + f_array_length_t i = 0; for (;;) { - - // @todo depend on a posix mutex condition that will designate when to sleep and perform actions. - // when the condition is on/off, then the program will either sleep until condition is toggled or sleep a given interval. - // when sleeping a given interval, then after each interval expiration, perform process cleanups until there are no asynchronous processes to cleanup. - // once all asynchronous processes are cleaned up, toggle the condition again and wait indefinitely. sleep(controller_thread_cache_cleanup_interval_long); if (f_thread_mutex_lock_try(&thread->mutex->cache) == F_none) { @@ -79,26 +98,34 @@ extern "C" { if (f_thread_mutex_lock_try(&thread->mutex->asynchronous) == F_none) { - for (i = 0; i < thread->asynchronouss.size; ++i) { + if (thread->asynchronouss.used) { + for (i = 0; i < thread->asynchronouss.used; ++i) { - if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) { - f_thread_join(thread->asynchronouss.array[i].id, 0); - thread->asynchronouss.array[i].state = controller_asynchronous_state_joined; - } + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) { + f_thread_join(thread->asynchronouss.array[i].id, 0); + thread->asynchronouss.array[i].state = controller_asynchronous_state_joined; + } - if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) { - controller_macro_cache_action_t_clear(thread->asynchronouss.array[i].cache); - thread->asynchronouss.array[i].state = 0; - } - } // for + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) { + controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]); - for (i = thread->asynchronouss.size; i; --i) { - if (thread->asynchronouss.array[i - 1].state) break; + thread->asynchronouss.array[i].state = 0; + } + + if (thread->asynchronouss.array[i].state) break; + } // for - controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]) - } // for + for (i = thread->asynchronouss.used - 1; thread->asynchronouss.used; --i, --thread->asynchronouss.used) { - thread->asynchronouss.used = i; + if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) { + controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]); + + thread->asynchronouss.array[i].state = 0; + } + + if (thread->asynchronouss.array[i].state) break; + } // for + } if (thread->asynchronouss.used < thread->asynchronouss.size) { controller_asynchronouss_resize(thread->asynchronouss.used, &thread->asynchronouss); @@ -151,7 +178,7 @@ extern "C" { if (f_file_exists(thread->setting->path_pid.string) == F_true) { if (thread->data->error.verbosity != f_console_verbosity_quiet) { - f_thread_mutex_lock(&thread->mutex->print); + if (thread->asynchronouss.enabled) f_thread_mutex_lock(&thread->mutex->print); fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]); fprintf(thread->data->error.to.stream, "%s%sThe pid file '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s); @@ -222,22 +249,28 @@ extern "C" { // only make the rule and control threads available once any/all pre-processing and is completed. if (F_status_is_error_not(status) && status != F_signal && status != F_child) { - controller_rule_wait_all(thread); + if (thread->data->parameters[controller_parameter_validate].result == f_console_result_none) { + controller_rule_wait_all(thread); - status = f_thread_create(0, &thread_rule, &controller_thread_rule, (void *) thread); + status = f_thread_create(0, &thread_rule, &controller_thread_rule, (void *) thread); - if (F_status_is_error_not(status)) { - status = f_thread_create(0, &thread_control, &controller_thread_control, (void *) thread); - } + if (F_status_is_error_not(status)) { + status = f_thread_create(0, &thread_control, &controller_thread_control, (void *) thread); + } - if (F_status_is_error(status)) { - if (thread->data->error.verbosity != f_console_verbosity_quiet) { - controller_error_print_locked(thread->data->error, F_status_set_fine(status), "f_thread_create", F_true, thread); + if (F_status_is_error(status)) { + if (thread->data->error.verbosity != f_console_verbosity_quiet) { + controller_error_print_locked(thread->data->error, F_status_set_fine(status), "f_thread_create", F_true, thread); + } } } } - if (F_status_is_error_not(status) && status != F_signal && status != F_child) { + if (status == F_child) { + return F_child; + } + + if (F_status_is_error_not(status) && status != F_signal && (thread->data->parameters[controller_parameter_validate].result == f_console_result_none || thread->data->parameters[controller_parameter_test].result == f_console_result_found)) { // wait until signal thread exits, which happens on any termination signal. f_thread_join(thread_signal, 0);