From ed0940dc80a011a48a3712f7e94ea52541f20105 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Wed, 1 May 2024 20:11:31 -0500 Subject: [PATCH] Progress: Continue migrating the project. --- data/build/dependencies | 1 + data/build/settings | 10 +- sources/c/main/common/string.c | 9 + sources/c/main/common/string.h | 17 + sources/c/main/common/type/global.h | 2 +- sources/c/main/common/type/lock.h | 7 +- sources/c/main/controller.h | 5 + sources/c/main/instance.c | 9 + sources/c/main/instance.h | 23 + sources/c/main/path.c | 1 - sources/c/main/rule.c | 9 + sources/c/main/rule.h | 23 + sources/c/main/rule/instance.c | 1076 +++++++++++++++++++++++++++++++++++ sources/c/main/rule/instance.h | 136 +++++ sources/c/main/thread.c | 2 +- sources/c/main/thread.h | 12 +- sources/c/main/thread/instance.c | 4 +- sources/c/main/thread/is.c | 14 +- sources/c/main/thread/is.h | 14 +- sources/c/main/time.c | 6 +- sources/c/main/time.h | 2 + 21 files changed, 1346 insertions(+), 36 deletions(-) create mode 100644 sources/c/main/instance.c create mode 100644 sources/c/main/instance.h create mode 100644 sources/c/main/rule.c create mode 100644 sources/c/main/rule.h create mode 100644 sources/c/main/rule/instance.c create mode 100644 sources/c/main/rule/instance.h diff --git a/data/build/dependencies b/data/build/dependencies index ca0cecf..37668f9 100644 --- a/data/build/dependencies +++ b/data/build/dependencies @@ -28,6 +28,7 @@ f_rip f_signal f_socket f_status_string +f_time f_thread fl_control_group diff --git a/data/build/settings b/data/build/settings index bed0aab..6dd2c98 100644 --- a/data/build/settings +++ b/data/build/settings @@ -35,7 +35,7 @@ build_libraries -lc -lcap build_libraries-individual -lfll_control_group -lfll_error -lfll_execute -lfll_fss -lfll_print -lfll_program -lfll_status_string build_libraries-individual_thread -lf_thread build_libraries-individual -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_fss -lfl_iki -lfl_path -lfl_print -build_libraries-individual -lf_account -lf_capability -lf_color -lf_compare -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_parse -lf_path -lf_pipe -lf_print -lf_rip -lf_signal -lf_socket -lf_status_string -lf_string -lf_type_array -lf_utf +build_libraries-individual -lf_account -lf_capability -lf_color -lf_compare -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_parse -lf_path -lf_pipe -lf_print -lf_rip -lf_signal -lf_socket -lf_status_string -lf_string -lf_time -lf_type_array -lf_utf build_libraries-individual_thread -lf_thread build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll @@ -43,18 +43,18 @@ build_libraries-monolithic -lfll build_sources_library main/common.c main/common/define.c main/common/enumeration.c main/common/print.c main/common/string.c main/common/type.c build_sources_library main/common/type/cache.c main/common/type/control.c main/common/type/entry.c main/common/type/global.c main/common/type/lock.c main/common/type/instance.c main/common/type/program.c main/common/type/rule.c main/common/type/thread.c build_sources_library main/common/string/general.c main/common/string/rule.c -build_sources_library main/path.c +build_sources_library main/path.c main/rule.c main/rule/instance.c build_sources_library main/print/data.c main/print/debug.c main/print/error.c main/print/lock.c main/print/message.c main/print/verbose.c main/print/warning.c -build_sources_library main/signal.c main/thread.c main/thread/instance.c main/thread/is.c +build_sources_library main/signal.c main/time.c main/thread.c main/thread/instance.c main/thread/is.c build_sources_headers main/common.h main/controller.h main/common/define.h main/common/enumeration.h main/common/print.h main/common/string.h main/common/thread.h main/common/type.h build_sources_headers main/common/define/control.h main/common/define/entry.h main/common/define/rule.h main/common/define/thread.h build_sources_headers main/common/enumeration/control.h main/common/enumeration/entry.h main/common/enumeration/instance.h main/common/enumeration/program.h main/common/enumeration/rule.h main/common/enumeration/thread.h build_sources_headers main/common/string/general.h main/common/string/rule.h build_sources_headers main/common/type/cache.h main/common/type/control.h main/common/type/entry.h main/common/type/global.h main/common/type/lock.h main/common/type/instance.h main/common/type/program.h main/common/type/rule.h main/common/type/thread.h -build_sources_headers main/path.h +build_sources_headers main/path.h main/rule.h main/rule/instance.h build_sources_headers main/print/data.h main/print/debug.h main/print/error.h main/print/lock.h main/print/message.h main/print/verbose.h main/print/warning.h -build_sources_headers main/signal.h main/thread.h main/thread/instance.h main/thread/is.h +build_sources_headers main/signal.h main/time.h main/thread.h main/thread/instance.h main/thread/is.h build_sources_documentation man diff --git a/sources/c/main/common/string.c b/sources/c/main/common/string.c index ed85272..6c32c05 100644 --- a/sources/c/main/common/string.c +++ b/sources/c/main/common/string.c @@ -30,6 +30,15 @@ extern "C" { const f_string_static_t controller_long_validate_s = macro_f_string_static_t_initialize_1(CONTROLLER_long_validate_s, 0, CONTROLLER_long_validate_s_length); #endif // _di_controller_parameter_s_ +/** + * Special strings used for rules. + */ +#ifndef _di_controller_rule_s_ + const f_string_static_t controller_rule_needed_s = macro_f_string_static_t_initialize_1(CONTROLLER_rule_needed_s, 0, CONTROLLER_rule_needed_s_length); + const f_string_static_t controller_rule_wanted_s = macro_f_string_static_t_initialize_1(CONTROLLER_rule_wanted_s, 0, CONTROLLER_rule_wanted_s_length); + const f_string_static_t controller_rule_wished_s = macro_f_string_static_t_initialize_1(CONTROLLER_rule_wished_s, 0, CONTROLLER_rule_wished_s_length); +#endif // _di_controller_rule_s_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/sources/c/main/common/string.h b/sources/c/main/common/string.h index 72f1d19..a335bd5 100644 --- a/sources/c/main/common/string.h +++ b/sources/c/main/common/string.h @@ -210,6 +210,23 @@ extern "C" { extern const f_string_static_t controller_default_path_socket_suffix_s; #endif // _di_controller_default_s_ +/** + * Special strings used for rules. + */ +#ifndef _di_controller_rule_s_ + #define CONTROLLER_rule_needed_s "needed" + #define CONTROLLER_rule_wanted_s "wanted" + #define CONTROLLER_rule_wished_s "wished for" + + #define CONTROLLER_rule_needed_s_length 6 + #define CONTROLLER_rule_wanted_s_length 6 + #define CONTROLLER_rule_wished_s_length 10 + + extern const f_string_static_t controller_rule_needed_s; + extern const f_string_static_t controller_rule_wanted_s; + extern const f_string_static_t controller_rule_wished_s; +#endif // _di_controller_rule_s_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/sources/c/main/common/type/global.h b/sources/c/main/common/type/global.h index ad98ec2..c770b0e 100644 --- a/sources/c/main/common/type/global.h +++ b/sources/c/main/common/type/global.h @@ -21,7 +21,7 @@ extern "C" { * * main: The main program data. * program: The program data. - * thread: The thread data. + * thread: The thread data for a specific thread. */ #ifndef _di_controller_global_t_ typedef struct { diff --git a/sources/c/main/common/type/lock.h b/sources/c/main/common/type/lock.h index 6e1b291..56fb5a2 100644 --- a/sources/c/main/common/type/lock.h +++ b/sources/c/main/common/type/lock.h @@ -15,19 +15,20 @@ #ifdef __cplusplus extern "C" { #endif + /** * A structure for sharing mutexes globally between different threads. * * The alert lock is intended for a generic waiting on alerts operations. * The cancel lock is intended for preventing double cancellation calls (which can happen due to interrupts). * The print lock is intended to lock any activity printing to stdout/stderr. - * The process lock is intended to lock any activity on the process structure. + * The instance lock is intended to lock any activity on the instance structure. * The rule lock is intended to lock any activity on the rules structure. * * alert: The alert mutex lock for waking up on alerts. * cancel: The cancel mutex lock for locking the cancel operation. * print: The print mutex lock. - * process: The process r/w lock. + * instance: The instance r/w lock. * rule: The rule r/w lock. * alert_condition: The condition used to trigger alerts. */ @@ -37,7 +38,7 @@ extern "C" { f_thread_mutex_t cancel; f_thread_mutex_t print; - f_thread_lock_t process; + f_thread_lock_t instance; f_thread_lock_t rule; f_thread_condition_t alert_condition; diff --git a/sources/c/main/controller.h b/sources/c/main/controller.h index 2743bcd..5d269a8 100644 --- a/sources/c/main/controller.h +++ b/sources/c/main/controller.h @@ -44,6 +44,7 @@ #include #include #include +#include #ifndef _di_thread_support_ #include @@ -96,9 +97,13 @@ #include #include #include +#include #include #include #include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/sources/c/main/instance.c b/sources/c/main/instance.c new file mode 100644 index 0000000..b4400f9 --- /dev/null +++ b/sources/c/main/instance.c @@ -0,0 +1,9 @@ +#include "controller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/main/instance.h b/sources/c/main/instance.h new file mode 100644 index 0000000..8916aa5 --- /dev/null +++ b/sources/c/main/instance.h @@ -0,0 +1,23 @@ +/** + * FLL - Level 3 + * + * Project: Controller + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides instance functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _controller_main_instance_h +#define _controller_main_instance_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _controller_main_instance_h diff --git a/sources/c/main/path.c b/sources/c/main/path.c index d113367..e3dd8e7 100644 --- a/sources/c/main/path.c +++ b/sources/c/main/path.c @@ -34,7 +34,6 @@ extern "C" { } #endif // _di_controller_path_canonical_relative_ - #ifdef __cplusplus } // extern "C" #endif diff --git a/sources/c/main/rule.c b/sources/c/main/rule.c new file mode 100644 index 0000000..b4400f9 --- /dev/null +++ b/sources/c/main/rule.c @@ -0,0 +1,9 @@ +#include "controller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/main/rule.h b/sources/c/main/rule.h new file mode 100644 index 0000000..db45e42 --- /dev/null +++ b/sources/c/main/rule.h @@ -0,0 +1,23 @@ +/** + * FLL - Level 3 + * + * Project: Controller + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides rule functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _controller_main_rule_h +#define _controller_main_rule_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _controller_main_rule_h diff --git a/sources/c/main/rule/instance.c b/sources/c/main/rule/instance.c new file mode 100644 index 0000000..884baad --- /dev/null +++ b/sources/c/main/rule/instance.c @@ -0,0 +1,1076 @@ +#include "../controller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_controller_rule_instance_ + f_status_t controller_rule_instance(const controller_global_t global, controller_instance_t * const instance) { + + switch (instance->action) { + case controller_rule_action_type_freeze_e: + case controller_rule_action_type_kill_e: + case controller_rule_action_type_pause_e: + case controller_rule_action_type_reload_e: + case controller_rule_action_type_restart_e: + case controller_rule_action_type_resume_e: + case controller_rule_action_type_start_e: + case controller_rule_action_type_stop_e: + case controller_rule_action_type_thaw_e: + break; + + default: + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + fl_print_format("%r%[%QUnsupported action type '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_rule_action_type_name(instance->action), global.main->program.error.notable); + fl_print_format("%[' while attempting to execute rule.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s); + + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + + return F_status_set_error(F_parameter); + } + + f_status_t status = F_okay; + f_status_t status_lock = F_okay; + + instance->cache.action.name_action.used = 0; + instance->cache.action.name_item.used = 0; + instance->cache.action.name_file.used = 0; + + status = f_string_dynamic_append(controller_rules_s, &instance->cache.action.name_file); + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(f_path_separator_s, &instance->cache.action.name_file); + } + + if (F_status_is_error(status)) { + controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true); + + return status; + } + + status = f_string_dynamic_append(instance->rule.alias, &instance->cache.action.name_file); + + if (F_status_is_error(status)) { + controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true); + + return status; + } + + status = f_string_dynamic_append(f_path_extension_separator_s, &instance->cache.action.name_file); + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(controller_rule_s, &instance->cache.action.name_file); + } + + if (F_status_is_error(status)) { + controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true); + + return status; + } + + if ((instance->options & controller_instance_option_simulate_d) && (instance->options & controller_instance_option_validate_d)) { + controller_rule_validate(global, instance->rule, instance->action, instance->options, &instance->cache); + } + + f_number_unsigned_t i = 0; + + { + f_number_unsigned_t j = 0; + f_number_unsigned_t id_rule = 0; + f_number_unsigned_t id_dependency = 0; + + bool found = F_false; + + controller_instance_t *dependency = 0; + + uint8_t options_instance = 0; + + const f_string_static_t strings[3] = { + controller_rule_needed_s, + controller_rule_wanted_s, + controller_rule_wished_s, + }; + + f_string_dynamics_t empty = f_string_dynamics_t_initialize; + f_string_dynamics_t *dynamics[3] = { &empty, &empty, &empty }; + + if (instance->action) { + + for (i = 0; i < instance->rule.ons.used; ++i) { + + if (instance->rule.ons.array[i].action == instance->action) { + dynamics[0] = &instance->rule.ons.array[i].need; + dynamics[1] = &instance->rule.ons.array[i].want; + dynamics[2] = &instance->rule.ons.array[i].wish; + + break; + } + } // for + } + + // i==0 is need, i==1 is want, i==2 is wish. + // Loop through all dependencies: wait for depedency, execute dependency, fail due to missing required dependency, or skip unrequired missing dependencies. + for (i = 0; i < 3 && controller_thread_is_enabled_instance(instance, global.thread); ++i) { + + for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_instance(instance, global.thread); ++j) { + + id_dependency = 0; + dependency = 0; + found = F_false; + + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.instance); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + } + else { + status = controller_instance_prepare_instance_type(global, instance->type, instance->action, dynamics[i]->array[j], &id_dependency); + + if (F_status_is_error(status)) { + if (F_status_set_fine(status) == F_lock) { + if (!controller_thread_is_enabled_instance_type(instance->type, global.thread)) { + return F_status_set_error(F_interrupt); + } + } + + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, dynamics[i]->array[j]); + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_false); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + + return status; + } + + status = F_true; + } + + if (status == F_true) { + found = F_true; + + dependency = global.thread->instances.array[id_dependency]; + + status_lock = controller_main_lock_read_instance(instance, global.thread, &dependency->active); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + status = F_false; + dependency = 0; + + f_thread_unlock(&global.thread->lock.instance); + } + else { + f_thread_unlock(&global.thread->lock.instance); + + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + status = F_false; + } + else { + status = controller_rule_find(dynamics[i]->array[j], global.setting->rules, &id_rule); + + f_thread_unlock(&global.thread->lock.rule); + } + } + } + else { + f_thread_unlock(&global.thread->lock.instance); + } + + if (status != F_true) { + found = F_false; + id_rule = 0; + + if (i == 0) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], dynamics[i]->array[j], "is not found"); + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + + status = F_status_set_error(F_found_not); + + if (!(instance->options & controller_instance_option_simulate_d)) { + if (dependency) { + f_thread_unlock(&dependency->active); + } + + break; + } + } + else { + if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) { + controller_lock_print(global.main->program.warning.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], dynamics[i]->array[j], "is not found"); + + controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.warning.to, global.thread); + } + } + } + else if (found) { + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + found = F_false; + status = status_lock; + } + } + + if (found) { + + // The dependency may have write locks, which needs to be avoided, so copy the alias from the rule. + f_string_static_t alias_other_buffer = f_string_static_t_initialize; + alias_other_buffer.used = global.setting->rules.array[id_rule].alias.used; + + f_char_t alias_other_buffer_string[alias_other_buffer.used + 1]; + alias_other_buffer.string = alias_other_buffer_string; + + memcpy(alias_other_buffer_string, global.setting->rules.array[id_rule].alias.string, sizeof(f_char_t) * alias_other_buffer.used); + alias_other_buffer_string[alias_other_buffer.used] = 0; + + f_thread_unlock(&global.thread->lock.rule); + + status_lock = controller_lock_read_instance(instance, global.thread, &dependency->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + status = status_lock; + } + else if (dependency->state == controller_instance_state_active_e || dependency->state == controller_instance_state_busy_e) { + f_thread_unlock(&dependency->lock); + + status = controller_instance_wait(global, dependency); + + if (F_status_is_error(status) && !(instance->options & controller_instance_option_simulate_d)) break; + + status = dependency->rule.status[instance->action]; + } + else { + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + f_thread_unlock(&dependency->lock); + + status = status_lock; + } + else if (controller_rule_status_is_available(instance->action, global.setting->rules.array[id_rule])) { + f_thread_unlock(&global.thread->lock.rule); + f_thread_unlock(&dependency->lock); + + options_instance = 0; + + if (global.main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) { + options_instance |= controller_instance_option_simulate_d; + } + + if (instance->options & controller_instance_option_validate_d) { + options_instance |= controller_instance_option_validate_d; + } + + // Synchronously execute dependency. + status = controller_rule_instance_begin(global, 0, alias_other_buffer, instance->action, options_instance, instance->type, instance->stack, dependency->cache); + + if (status == F_child || F_status_set_fine(status) == F_interrupt) { + f_thread_unlock(&dependency->active); + + break; + } + + if (F_status_is_error(status)) { + if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "failed during execution"); + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + + if (!(dependency->options & controller_instance_option_simulate_d) || F_status_set_fine(status) == F_memory_not) { + f_thread_unlock(&dependency->active); + + break; + } + } + else { + if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) { + controller_lock_print(global.main->program.warning.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], alias_other_buffer, "failed during execution"); + + controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.warning.to, global.thread); + } + } + } + } + else { + status = global.setting->rules.array[id_rule].status[instance->action]; + + f_thread_unlock(&global.thread->lock.rule); + f_thread_unlock(&dependency->lock); + } + } + + if (!controller_thread_is_enabled_instance(instance, global.thread)) { + f_thread_unlock(&dependency->active); + + break; + } + + if (F_status_is_error_not(status_lock)) { + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + } + } + + if (F_status_is_error(status_lock)) { + if (F_status_is_error(status_lock)) { + controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "due to lock failure"); + } + + status = status_lock; + } + else if (controller_rule_status_is_error(instance->action, global.setting->rules.array[id_rule])) { + f_thread_unlock(&global.thread->lock.rule); + + if (i == 0 || i == 1) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "is in a failed state"); + + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + + status = F_status_set_error(F_found_not); + + if (!(dependency->options & controller_instance_option_simulate_d)) { + f_thread_unlock(&dependency->active); + + break; + } + } + else { + if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) { + controller_lock_print(global.main->program.warning.to, global.thread); + + controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], alias_other_buffer, "is in a failed state"); + + controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.warning.to, global.thread); + } + } + } + else { + f_thread_unlock(&global.thread->lock.rule); + } + } + + if (dependency) { + f_thread_unlock(&dependency->active); + } + } // for + + if (status == F_child || F_status_set_fine(status) == F_interrupt) break; + + if (F_status_is_error(status) && !(instance->options & controller_instance_option_simulate_d)) break; + } // for + } + + if (status == F_child || F_status_set_fine(status) == F_interrupt) { + return status; + } + + if (!controller_thread_is_enabled_instance(instance, global.thread)) { + return F_status_set_error(F_interrupt); + } + + if ((instance->options & controller_instance_option_wait_d) && F_status_is_error_not(status) && (instance->options & controller_instance_option_validate_d)) { + status_lock = controller_rule_wait_all_instance_type(global, instance->type, F_false); + + if (F_status_set_fine(status_lock) == F_interrupt) { + return status_lock; + } + } + + if (!(instance->options & controller_instance_option_validate_d) && F_status_is_error_not(status)) { + + // Find at least one of the requested action when the rule is required. + if (instance->options & controller_instance_option_require_d) { + bool missing = F_true; + + f_number_unsigned_t j = 0; + + for (i = 0; i < instance->rule.items.used; ++i) { + + for (j = 0; j < instance->rule.items.array[i].actions.used; ++j) { + + if (instance->rule.items.array[i].actions.array[j].type == instance->action) { + missing = F_false; + + break; + } + } // for + } // for + + if (missing) { + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + if (instance->rule.items.used) { + fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context); + fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.name, global.main->program.error.notable); + fl_print_format("%[' has no '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_rule_action_type_name(instance->action), global.main->program.error.notable); + fl_print_format("%[' action to execute.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s); + } + else { + fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context); + fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.name, global.main->program.error.notable); + fl_print_format("%[ has no known '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format("%[%r %r%]", global.main->program.error.to, global.main->program.error.notable, controller_rule_s, controller_type_s, global.main->program.error.notable); + fl_print_format("%[' (such as '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_command_s, global.main->program.error.notable); + fl_print_format("%[', '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_service_s, global.main->program.error.notable); + fl_print_format("%[', '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_script_s, global.main->program.error.notable); + fl_print_format("%[', or '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context); + fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_utility_s, global.main->program.error.notable); + fl_print_format("%[') to execute.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s); + } + + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + + status = F_status_set_error(F_parameter); + } + } + + if (F_status_is_error_not(status)) { + status = controller_rule_execute(global, instance->action, instance->options, instance); + + if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) { + return status; + } + + if (F_status_is_error(status)) { + controller_rule_item_print_error(global.thread, &global.main->program.error, instance->cache.action, F_true, F_status_set_fine(status)); + } + } + } + + f_number_unsigned_t id_rule = 0; + + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + if (F_status_set_fine(status) != F_interrupt) { + status = controller_lock_read_instance(instance, global.thread, &instance->lock); + if (F_status_is_error_not(status)) return status_lock; + } + + return F_status_set_error(F_lock); + } + + if (F_status_is_error(status)) { + instance->rule.status[instance->action] = controller_status_simplify_error(F_status_set_fine(status)); + } + else { + instance->rule.status[instance->action] = status; + } + + status_lock = controller_lock_write_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + f_thread_unlock(&instance->lock); + + status = controller_lock_read_instance(instance, global.thread, &instance->lock); + if (F_status_is_error_not(status)) return status_lock; + + return F_status_set_error(F_lock); + } + + // Update the global rule status, which is stored separately from the rule status for this instance. + if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) { + controller_rule_t *rule = &global.setting->rules.array[id_rule]; + + rule->status[instance->action] = instance->rule.status[instance->action]; + + f_number_unsigned_t j = 0; + + controller_rule_item_t *rule_item = 0; + + // Copy all rule item action statuses from the rule instance to the rule. + for (i = 0; i < rule->items.used; ++i) { + + rule_item = &rule->items.array[i]; + + for (j = 0; j < rule_item->actions.used; ++j) { + rule_item->actions.array[j].status = instance->rule.items.array[i].actions.array[j].status; + } // for + } // for + } + + f_thread_unlock(&global.thread->lock.rule); + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + return F_status_set_error(F_lock); + } + + return instance->rule.status[instance->action]; + } +#endif // _di_controller_rule_instance_ + +#ifndef _di_controller_rule_instance_begin_ + f_status_t controller_rule_instance_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache) { + + if (!controller_thread_is_enabled_instance_type(type, global.thread)) { + return F_status_set_error(F_interrupt); + } + + f_status_t status = F_okay; + f_status_t status_lock = F_okay; + + controller_instance_t *instance = 0; + + status = controller_lock_read_instance_type(type, global.thread, &global.thread->lock.instance); + + if (F_status_is_error(status)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status), F_true, global.thread); + + return status; + } + + { + f_number_unsigned_t at = 0; + + status = controller_instance_prepare(global, type != controller_instance_type_exit_e, action, alias_rule, &at); + + if (F_status_is_error(status)) { + f_thread_unlock(&global.thread->lock.instance); + + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, alias_rule); + controller_rule_print_error_cache(&global.main->program.error, cache.action, F_false); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + + return status; + } + + instance = global.thread->instances.array[at]; + + status = controller_lock_read_instance_type(type, global.thread, &instance->active); + + if (F_status_is_error(status)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status), F_true, global.thread); + controller_rule_item_print_error(global.thread, &global.main->program.error, cache.action, F_false, F_status_set_fine(status)); + + f_thread_unlock(&global.thread->lock.instance); + + return status; + } + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + f_thread_unlock(&instance->active); + f_thread_unlock(&global.thread->lock.instance); + + return status_lock; + } + + // Once a write lock on the instance is achieved, it is safe to unlock the global instance read lock. + f_thread_unlock(&global.thread->lock.instance); + + // If the instance is already running, then there is nothing to do. + if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e) { + f_thread_unlock(&instance->lock); + f_thread_unlock(&instance->active); + + return F_busy; + } + + // The thread is done, so close the thread. + if (instance->state == controller_instance_state_done_e) { + controller_thread_join(&instance->id_thread); + + f_thread_mutex_lock(&instance->wait_lock); + f_thread_condition_signal_all(&instance->wait); + f_thread_mutex_unlock(&instance->wait_lock); + } + + instance->id = at; + } + + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + f_thread_unlock(&instance->active); + + return status_lock; + } + + instance->state = controller_instance_state_active_e; + instance->action = action; + instance->options = options; + instance->type = type; + + macro_f_time_simple_t_clear(instance->cache.timestamp) + + instance->cache.ats.used = 0; + instance->cache.stack.used = 0; + instance->cache.comments.used = 0; + instance->cache.delimits.used = 0; + instance->cache.content_action.used = 0; + instance->cache.content_actions.used = 0; + instance->cache.content_items.used = 0; + instance->cache.object_actions.used = 0; + instance->cache.object_items.used = 0; + instance->cache.buffer_file.used = 0; + instance->cache.buffer_item.used = 0; + instance->cache.buffer_path.used = 0; + instance->cache.expanded.used = 0; + instance->cache.action.line_action = cache.action.line_action; + instance->cache.action.line_item = cache.action.line_item; + instance->cache.action.name_action.used = 0; + instance->cache.action.name_file.used = 0; + instance->cache.action.name_item.used = 0; + instance->cache.action.generic.used = 0; + instance->cache.range_action.start = 1; + instance->cache.range_action.stop = 0; + + instance->stack.used = 0; + + instance->main_data = (void *) global.main; + instance->main_setting = (void *) global.setting; + instance->main_thread = (void *) global.thread; + + if (F_status_is_error_not(status) && stack.used) { + if (instance->stack.size < stack.used) { + status = f_memory_array_resize(stack.used, sizeof(f_number_unsigned_t), (void **) &instance->stack.array, &instance->stack.used, &instance->stack.size); + } + + if (F_status_is_error(status)) { + controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_memory_array_resize", F_true); + } + else { + for (f_number_unsigned_t i = 0; i < stack.used; ++i) { + instance->stack.array[i] = stack.array[i]; + } // for + + instance->stack.used = stack.used; + } + } + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(cache.action.name_action, &instance->cache.action.name_action); + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(cache.action.name_file, &instance->cache.action.name_file); + } + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(cache.action.name_item, &instance->cache.action.name_item); + } + else { + controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_string_dynamic_append", F_true); + } + } + + f_thread_unlock(&instance->lock); + + if (F_status_is_error_not(status)) { + if (instance->action && (options_force & controller_instance_option_asynchronous_d)) { + if (instance->type == controller_instance_type_exit_e) { + status = f_thread_create(0, &instance->id_thread, controller_thread_instance_other, (void *) instance); + } + else { + status = f_thread_create(0, &instance->id_thread, controller_thread_instance_normal, (void *) instance); + } + + if (F_status_is_error(status)) { + controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_thread_create", F_true); + } + } + else { + status = controller_rule_instance_do(options_force, instance); + + if (status == F_child || F_status_set_fine(status) == F_interrupt) { + f_thread_unlock(&instance->active); + + return status; + } + } + } + + if (!action || F_status_is_error(status) && (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) { + { + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + f_thread_unlock(&instance->active); + + return status_lock; + } + } + + if (!action || (options_force & controller_instance_option_asynchronous_d)) { + instance->state = controller_instance_state_done_e; + } + else { + instance->state = controller_instance_state_idle_e; + } + + f_thread_mutex_lock(&instance->wait_lock); + f_thread_condition_signal_all(&instance->wait); + f_thread_mutex_unlock(&instance->wait_lock); + + f_thread_unlock(&instance->lock); + } + + f_thread_unlock(&instance->active); + + if (F_status_is_error(status)) { + return status; + } + + return F_okay; + } +#endif // _di_controller_rule_instance_begin_ + +#ifndef _di_controller_rule_instance_do_ + f_status_t controller_rule_instance_do(const uint8_t options_force, controller_instance_t * const instance) { + + f_status_t status_lock = F_okay; + + const controller_global_t global = macro_controller_global_t_initialize_1((controller_main_t *) instance->main_data, (controller_instance_t *) instance->main_setting, (controller_thread_t *) instance->main_thread); + + // The instance and active locks shall be held for the duration of this instanceing (aside from switching between read to/from write). + if (options_force & controller_instance_option_asynchronous_d) { + status_lock = controller_lock_read_instance(instance, global.thread, &instance->active); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + return status_lock; + } + } + + status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + f_status_t status = F_okay; + + f_number_unsigned_t id_rule = 0; + + const f_number_unsigned_t used_original_stack = instance->stack.used; + + status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + f_thread_unlock(&instance->lock); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) { + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + f_thread_unlock(&global.thread->lock.rule); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + controller_rule_delete(&instance->rule); + + status = controller_rule_copy(global.setting->rules.array[id_rule], &instance->rule); + + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + f_thread_unlock(&global.thread->lock.rule); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + f_thread_unlock(&global.thread->lock.rule); + + if (F_status_is_error(status)) { + controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "controller_rule_copy", F_true); + } + else if (!instance->action) { + + // This is a "consider" Action, so do not actually execute the rule. + f_thread_unlock(&instance->lock); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return F_instance_not; + } + else { + for (f_number_unsigned_t i = 0; i < instance->stack.used && controller_thread_is_enabled_instance(instance, global.thread); ++i) { + + if (instance->stack.array[i] == id_rule) { + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context); + fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.alias, global.main->program.error.notable); + fl_print_format("%[' is already on the execution dependency stack, this recursion is prohibited.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s); + + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + + // Never continue on circular recursion errors even in simulate mode. + status = F_status_set_error(F_recurse); + + break; + } + } // for + + if (!controller_thread_is_enabled_instance(instance, global.thread)) { + f_thread_unlock(&instance->lock); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return F_status_set_error(F_interrupt); + } + + if (F_status_is_error_not(status)) { + status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_number_unsigned_t), (void **) &instance->stack.array, &instance->stack.used, &instance->stack.size); + + if (F_status_is_error(status)) { + controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_memory_array_increase", F_true); + } + else { + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + instance->stack.array[instance->stack.used++] = id_rule; + + f_thread_unlock(&instance->lock); + + status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + } + } + } + + if (F_status_is_error_not(status)) { + status = controller_rule_instance(global, instance); + } + } + else { + f_thread_unlock(&global.thread->lock.rule); + + status = F_status_set_error(F_found_not); + + if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) { + controller_lock_print(global.main->program.error.to, global.thread); + + controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, instance->rule.alias); + controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_false); + + controller_unlock_print_flush(global.main->program.error.to, global.thread); + } + } + + if (status == F_child) { + f_thread_unlock(&instance->lock); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status; + } + + status_lock = controller_lock_write_instance(instance, global.thread, &global.thread->lock.rule); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + if (F_status_set_fine(status) != F_lock) { + f_thread_unlock(&instance->lock); + } + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + if (F_status_set_fine(status) == F_lock) { + if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) { + global.setting->rules.array[id_rule].status[instance->action] = status; + } + } + + f_thread_unlock(&global.thread->lock.rule); + + if (F_status_set_fine(status) != F_lock) { + f_thread_unlock(&instance->lock); + } + + if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_instance(instance, global.thread)) { + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return F_status_set_error(F_interrupt); + } + + status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock); + + if (F_status_is_error(status_lock)) { + controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + return status_lock; + } + + if (options_force & controller_instance_option_asynchronous_d) { + instance->state = controller_instance_state_done_e; + } + else { + instance->state = controller_instance_state_idle_e; + } + + instance->stack.used = used_original_stack; + + // inform all things waiting that the instance has finished running. + f_thread_mutex_lock(&instance->wait_lock); + f_thread_condition_signal_all(&instance->wait); + f_thread_mutex_unlock(&instance->wait_lock); + + f_thread_unlock(&instance->lock); + + if (options_force & controller_instance_option_asynchronous_d) { + f_thread_unlock(&instance->active); + } + + if (controller_thread_is_enabled_instance(instance, global.thread)) { + return status; + } + + return F_status_set_error(F_interrupt); + } +#endif // _di_controller_rule_instance_do_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sources/c/main/rule/instance.h b/sources/c/main/rule/instance.h new file mode 100644 index 0000000..f77e8b6 --- /dev/null +++ b/sources/c/main/rule/instance.h @@ -0,0 +1,136 @@ +/** + * FLL - Level 3 + * + * Project: Controller + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides the rule "instance" functionality. + * + * This is auto-included and should not need to be explicitly included. + */ +#ifndef _controller_main_rule_instance_h +#define _controller_main_rule_instance_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Process and execute the given rule. + * + * Any dependent rules are processed and executed as per "need", "want", and "wish" rule settings. + * All dependent rules must be already loaded, this function will not load any rules. + * + * This requires that a read lock be set on instance->lock before being called. + * + * This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_number_unsigneds_t array. + * + * The rule status will be updated by this function. + * + * @param global + * The global data. + * @param instance + * The instance data for processing this rule. + * + * @return + * F_okay on success. + * F_child on child instance exiting. + * F_failure on execution failure. + * + * F_interrupt (with error bit) on receiving a instance signal, such as an interrupt signal. + * F_lock (with error bit) if failed to re-establish read lock on instance->lock while returning. + * + * Errors (with error bit) from: controller_lock_read(). + * Errors (with error bit) from: controller_lock_write(). + */ +#ifndef _di_controller_rule_instance_ + extern f_status_t controller_rule_instance(const controller_global_t global, controller_instance_t * const instance); +#endif // _di_controller_rule_instance_ + +/** + * Synchronously or asynchronously begin processing some rule. + * + * @param global + * The global data. + * @param options_force + * Force the given instance options, only supporting a subset of instance options. + * + * If controller_instance_option_asynchronous_d, then asynchronously execute. + * If not controller_instance_option_asynchronous_d, then synchronously execute. + * @param alias_rule + * The alias of the rule, such as "boot/init". + * @param action + * The action to perform based on the action type codes. + * @param options + * The instance options to pass to the instance. + * @param type + * The instance type, such as controller_data_type_entry_e. + * @param stack + * A stack representing the instances already running in this rule instance dependency tree. + * This is used to prevent circular dependencies. + * @param cache + * A structure for containing and caching relevant data. + * + * @return + * F_okay on success. + * F_busy on success and the instance is found to already be running (nothing to do). + * + * F_found_not (with error bit) if unable to for a instance for the given rule id. + * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal. + * F_recurse (with error bit) on recursion error (the instance is already on the instance stack). + * + * Status from: controller_rule_instance(). + * + * Errors (with error bit) from: controller_rule_instance(). + * Errors (with error bit) from: f_string_dynamic_append(). + * Errors (with error bit) from: f_thread_create(). + * + * @see controller_rule_instance() + * @see f_string_dynamic_append() + * @see f_thread_create() + */ +#ifndef _di_controller_rule_instance_begin_ + extern f_status_t controller_rule_instance_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache); +#endif // _di_controller_rule_instance_begin_ + +/** + * Helper for calling controller_rule_instance(). + * + * This does all the preparation work that needs to be synchronously performed within the same thread. + * This will copy the rule by the alias to the instance structure. + * + * @param options_force + * Force the given instance options, only supporting a subset of instance options. + * + * If controller_instance_option_asynchronous_d, then asynchronously execute. + * If not controller_instance_option_asynchronous_d, then synchronously execute. + * @param instance + * The instance data. + * + * @return + * F_okay on success. + * F_found on the instance was found to already be running (nothing to do). + * F_process_not if the instance was not executed because it is a "consider" Action. + * + * F_found_not (with error bit) if unable to for a instance for the given rule id. + * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal. + * + * Status from: controller_rule_instance(). + * + * Errors (with error bit) from: controller_rule_copy(). + * Errors (with error bit) from: controller_rule_instance(). + * + * @see controller_rule_copy() + * @see controller_rule_instance() + * @see controller_rule_instance_begin() + */ +#ifndef _di_controller_rule_instance_do_ + extern f_status_t controller_rule_instance_do(const uint8_t options_force, controller_instance_t * const instance); +#endif // _di_controller_rule_instance_do_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _controller_main_rule_instance_h diff --git a/sources/c/main/thread.c b/sources/c/main/thread.c index 29c20c0..4a69cec 100644 --- a/sources/c/main/thread.c +++ b/sources/c/main/thread.c @@ -87,7 +87,7 @@ extern "C" { #endif // _di_controller_main_thread_signal_normal_ #ifndef _di_controller_main_thread_signal_other_ - void * controller_main_thread_signal_other(void * const arguments) { + void * controller_main_thread_signal_other(void * const global) { f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0); diff --git a/sources/c/main/thread.h b/sources/c/main/thread.h index 3290be0..5856483 100644 --- a/sources/c/main/thread.h +++ b/sources/c/main/thread.h @@ -64,8 +64,8 @@ /** * Thread for handling signals/interrupts during normal operations. * - * @param arguments - * The thread arguments. + * @param global + * The global structure. * Must be of type controller_global_t. * * @return @@ -74,14 +74,14 @@ * @see controller_main_thread_signal() */ #ifndef _di_controller_main_thread_signal_normal_ - extern void * controller_main_thread_signal_normal(void * const arguments); + extern void * controller_main_thread_signal_normal(void * const global); #endif // _di_controller_main_thread_signal_normal_ /** * Thread for handling signals/interrupts during other operations. * - * @param arguments - * The thread arguments. + * @param global + * The global structure. * Must be of type controller_global_t. * * @return @@ -90,7 +90,7 @@ * @see controller_main_thread_signal() */ #ifndef _di_controller_main_thread_signal_other_ - extern void * controller_main_thread_signal_other(void * const arguments); + extern void * controller_main_thread_signal_other(void * const global); #endif // _di_controller_main_thread_signal_other_ #ifdef __cplusplus diff --git a/sources/c/main/thread/instance.c b/sources/c/main/thread/instance.c index 3da4a86..5ae10d8 100644 --- a/sources/c/main/thread/instance.c +++ b/sources/c/main/thread/instance.c @@ -8,7 +8,7 @@ extern "C" { void controller_thread_instance(const uint8_t is_normal, controller_instance_t * const instance) { if (!instance) return; - if (!controller_thread_is_enabled(is_normal, instance->main_thread)) return; + if (!controller_main_thread_is_enabled(is_normal, (controller_thread_t *) instance->thread)) return; const f_status_t status = controller_rule_process_do(controller_process_option_asynchronous_d, instance); @@ -34,7 +34,7 @@ extern "C" { f_thread_mutex_lock(&global->thread->lock.cancel); // Only cancel when enabled. - if (!controller_thread_is_enabled(is_normal, global->thread)) { + if (!controller_main_thread_is_enabled(is_normal, global->thread)) { f_thread_mutex_unlock(&global->thread->lock.cancel); return; diff --git a/sources/c/main/thread/is.c b/sources/c/main/thread/is.c index bfa4d09..02a594f 100644 --- a/sources/c/main/thread/is.c +++ b/sources/c/main/thread/is.c @@ -13,21 +13,21 @@ extern "C" { } #endif // _di_controller_main_thread_is_enabled_ -#ifndef _di_controller_main_thread_is_enabled_process_ - f_status_t controller_main_thread_is_enabled_process(controller_instance_t * const instance, controller_thread_t * const thread) { +#ifndef _di_controller_main_thread_is_enabled_instance_ + f_status_t controller_main_thread_is_enabled_instance(controller_instance_t * const instance, controller_thread_t * const thread) { if (!instance) return F_false; - return controller_main_thread_is_enabled_process_type(instance->type, thread); + return controller_main_thread_is_enabled_instance_type(instance->type, thread); } -#endif // _di_controller_main_thread_is_enabled_process_ +#endif // _di_controller_main_thread_is_enabled_instance_ -#ifndef _di_controller_main_thread_is_enabled_process_type_ - f_status_t controller_main_thread_is_enabled_process_type(const uint8_t type, controller_thread_t * const thread) { +#ifndef _di_controller_main_thread_is_enabled_instance_type_ + f_status_t controller_main_thread_is_enabled_instance_type(const uint8_t type, controller_thread_t * const thread) { return controller_main_thread_is_enabled(type != controller_data_type_exit_e, thread); } -#endif // _di_controller_main_thread_is_enabled_process_type_ +#endif // _di_controller_main_thread_is_enabled_instance_type_ #ifdef __cplusplus } // extern "C" diff --git a/sources/c/main/thread/is.h b/sources/c/main/thread/is.h index 3a07baa..f7fff4c 100644 --- a/sources/c/main/thread/is.h +++ b/sources/c/main/thread/is.h @@ -20,8 +20,8 @@ extern "C" { * Check to see if thread is enabled for the normal operations like entry and control or for exit operations. * * @param is_normal - * If TRUE, then process as if this operates during a normal operation (entry and control). - * If FALSE, then process as if this operates during a an exit operation. + * If TRUE, then instance as if this operates during a normal operation (entry and control). + * If FALSE, then instance as if this operates during a an exit operation. * @param thread * The thread data. * @@ -34,7 +34,7 @@ extern "C" { #endif // _di_controller_main_thread_is_enabled_ /** - * Check to see if thread is enabled for the normal operations like entry and control or for exit operations for some process. + * Check to see if thread is enabled for the normal operations like entry and control or for exit operations for some instance. * * @param instance * The instance to use when checking if thread is enabled. @@ -45,11 +45,11 @@ extern "C" { * F_true when enabled. * F_false when disabled or when parameter is invalid.. * - * @see controller_main_thread_is_enabled_process_type() + * @see controller_main_thread_is_enabled_instance_type() */ -#ifndef _di_controller_main_thread_is_enabled_process_ - extern f_status_t controller_main_thread_is_enabled_process(controller_instance_t * const instance, controller_thread_t * const thread); -#endif // _di_controller_main_thread_is_enabled_process_ +#ifndef _di_controller_main_thread_is_enabled_instance_ + extern f_status_t controller_main_thread_is_enabled_instance(controller_instance_t * const instance, controller_thread_t * const thread); +#endif // _di_controller_main_thread_is_enabled_instance_ #ifdef __cplusplus } // extern "C" diff --git a/sources/c/main/time.c b/sources/c/main/time.c index 99b7205..19ee1cd 100644 --- a/sources/c/main/time.c +++ b/sources/c/main/time.c @@ -52,10 +52,10 @@ extern "C" { #ifndef _di_controller_time_sleep_nanoseconds_ f_status_t controller_time_sleep_nanoseconds(controller_global_t * const global, const f_time_spec_t time) { - if (!global || !global->setting) return F_status_set_error(F_parameter); + if (!global || !global->main) return F_status_set_error(F_parameter); // When sleep is a second or more, instead wait for terminating signals if interruptible. - if ((global->main->setting.flag & controller_setting_flag_interruptible_e) && time.tv_sec) { + if ((global->main->setting.flag & controller_main_flag_interruptible_e) && time.tv_sec) { siginfo_t information; f_signal_t signal = f_signal_t_initialize; @@ -70,7 +70,7 @@ extern "C" { return f_signal_wait_until(&signal.set, &time, &information); } - return f_time_sleep_spec(time, remaining); + return f_time_sleep_spec(time, 0); } #endif // _di_controller_time_sleep_nanoseconds_ diff --git a/sources/c/main/time.h b/sources/c/main/time.h index 644e9bb..651b6bc 100644 --- a/sources/c/main/time.h +++ b/sources/c/main/time.h @@ -43,6 +43,8 @@ * Success from: f_signal_wait_until(). * Success from: f_time_of_day_get(). * + * F_parameter (with error bit) on invalid parameter. + * * Errors (with error bit) from: f_signal_wait_until(). * Errors (with error bit) from: f_time_of_day_get(). * -- 1.8.3.1