From 7294a73da844aa4b8dbb38afadfaa2ffe5e2828f Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 23 Mar 2021 22:57:47 -0500 Subject: [PATCH] Progress: controller program. --- level_3/controller/c/private-common.c | 8 +- level_3/controller/c/private-common.h | 306 ++++++++++++++++++++++++------ level_3/controller/c/private-controller.c | 186 +++++++----------- level_3/controller/c/private-entry.c | 4 +- level_3/controller/c/private-rule.c | 154 ++++++++++++--- level_3/controller/c/private-rule.h | 6 +- level_3/controller/c/private-thread.c | 92 ++++++--- level_3/controller/c/private-thread.h | 6 +- 8 files changed, 532 insertions(+), 230 deletions(-) diff --git a/level_3/controller/c/private-common.c b/level_3/controller/c/private-common.c index 3ed2c03..4a5e6ae 100644 --- a/level_3/controller/c/private-common.c +++ b/level_3/controller/c/private-common.c @@ -150,11 +150,13 @@ extern "C" { #ifndef _di_controller_process_delete_simple_ void controller_process_delete_simple(controller_process_t *process) { - f_string_dynamic_resize(0, &process->id); + f_string_dynamic_resize(0, &process->id_rule); f_thread_lock_delete(&process->lock); - f_thread_lock_attribute_delete(&process->attribute); - f_thread_condition_delete(&process->wait); + f_thread_lock_delete(&process->active); + + f_thread_lock_attribute_delete(&process->lock_attribute); + f_thread_lock_attribute_delete(&process->active_attribute); } #endif // _di_controller_process_delete_simple_ diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index df59d78..ac709f1 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -12,6 +12,11 @@ extern "C" { #endif +/** + * All special strings used within this program. + * + * These are generally the names to match, representing some action or setting. + */ #ifndef _di_controller_string_ #define controller_string_action "action" #define controller_string_actions "actions" @@ -274,6 +279,11 @@ extern "C" { const static f_string_t controller_string_yes_s = controller_string_yes; #endif // _di_controller_string_ +/** + * A set of codes for resource limitations. + * + * This essentally converts the POSIX standard names into a more verbose format. + */ #ifndef _di_controller_resource_limit_t_ enum { controller_resource_limit_type_as = RLIMIT_AS, @@ -295,6 +305,12 @@ extern "C" { }; #endif // _di_controller_resource_limit_t_ +/** + * A structure for passing execution arguments to the execute functions. + * + * parameter: All parameters sent to the program on execution. + * as: All special properties to apply, such as cpu affinity. + */ #ifndef _di_controller_execute_set_t_ typedef struct { fl_execute_parameter_t parameter; @@ -319,10 +335,19 @@ extern "C" { /** * A structure for sharing mutexes globally between different threads. * - * The asynchronous lock is intended to lock any activity on the asynchronouss structure. + * The asynchronous lock is intended to lock any activity on the asynchronouss structure and related. * The print lock is intended to lock any activity printing to stdout/stderr. * The process lock is intended to lock any activity on the processs structure. * The rule lock is intended to lock any activity on the rules structure. + * + * print: The print mutex lock. + * asynchronous: The asynchronous r/w lock. + * asynchronous_attribute: The asynchronous r/w lock attribute. + * process: The process r/w lock. + * process_attribute: The process r/w lock attribute. + * rule: The rule r/w lock. + * rule_attribute: The rule r/w lock attribute. + * */ #ifndef _di_controller_lock_t_ typedef struct { @@ -349,6 +374,14 @@ extern "C" { } #endif // _di_controller_mutex_t_ +/** + * A Rule Action. + * + * type: The Rule Action type. + * line: The line number where the Rule Action begins. + * status: The last execution status of the Rule Action. + * parameters: All parameters associated with the Rule Action. + */ #ifndef _di_controller_rule_action_t_ #define controller_rule_action_method_string_extended "FSS-0001 (Extended)" #define controller_rule_action_method_string_extended_list "FSS-0003 (Extended List)" @@ -399,6 +432,13 @@ extern "C" { f_macro_string_dynamics_t_clear(rule.parameters) #endif // _di_controller_rule_action_t_ +/** + * The Rule Actions. + * + * array: An array of Rule Actions. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_rule_actions_t_ typedef struct { controller_rule_action_t *array; @@ -419,6 +459,13 @@ extern "C" { actions.used = 0; #endif // _di_controller_rule_actions_t_ +/** + * A Rule Item. + * + * type: The type of the Rule Item. + * line: The line number where the Rule Item begins. + * actions: The actions associated with the Rule Item. + */ #ifndef _di_controller_rule_item_t_ enum { controller_rule_item_type_command = 1, @@ -447,6 +494,13 @@ extern "C" { controller_macro_rule_actions_t_clear(item.actions); #endif // _di_controller_rule_item_t_ +/** + * The Rule Items. + * + * array: An array of Rule Items. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_rule_items_t_ typedef struct { controller_rule_item_t *array; @@ -467,6 +521,35 @@ extern "C" { items.used = 0; #endif // _di_controller_rule_items_t_ +/** + * A Rule. + * + * timeout_kill: The timeout to wait relating to using a kill signal. + * timeout_start: The timeout to wait relating to starting a process. + * timeout_stop: The timeout to wait relating to stopping a process. + * has: Bitwise set of "has" codes representing what the Rule has. + * nice: The niceness value if the Rule "has" nice. + * user: The User ID if the Rule "has" a user. + * group: The Group ID if the Rule "has" a group. + * timestamp: The timestamp when the Rule was loaded. + * id: The distinct ID (machine name) of the rule, such as "service/ssh". + * name: A human name for the Rule (does not have to be distinct). + * path: The path to the Rule file. + * script: The program or path to the program of the scripting engine to use when processing scripts in this Rule. + * define: Any defines (environment variables) made available to the Rule for IKI substitution or just as environment variables. + * parameters: Any parameters made available to the Rule for IKI substitution. + * environment: All environment variables allowed to be exposed to the Rule when processing. + * need: A Rule ID (machine name) of the Rule that is needed (required). + * want: A Rule ID (machine name) of the Rule that is wanted (optional). + * wish: A Rule ID (machine name) of the Rule that is wished for (optional). + * affinity: The cpu affinity to be used when executing the Rule. + * capability: The capability setting if the Rule "has" a capability. + * control_group: The control group setting if the Rule "has" a control group. + * groups: The groups to assign to the user to run as (with the first group being the primary group). + * limits: The cpu/resource limits to use when executing the Rule. + * scheduler: The scheduler setting if the Rule "has" a scheduler. + * items: All items associated with the Rule. + */ #ifndef _di_controller_rule_t_ enum { controller_rule_setting_type_affinity = 1, @@ -592,6 +675,13 @@ extern "C" { controller_macro_rule_items_t_clear(rule.items) #endif // _di_controller_rule_t_ +/** + * The Rules. + * + * array: An array of Rules. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_rules_t_ typedef struct { controller_rule_t *array; @@ -613,37 +703,46 @@ extern "C" { #endif // _di_controller_rules_t_ /** - * Store controller process information for some rule. + * A Rule Process. * - * This refers to "process" as in the processing of a rule and does not refer to "process" as in a CPU process. + * This refers to "process" as in the processing of a single rule for the given Rule ID and does not refer to "process" as in a CPU Process. * - * This holds the success/failure rate and any associated locks. - * This operates based on the rule id string (such as "network/ntpdate"). + * id_rule: The Rule ID, such as "network/ntpdate". + * status: The last execution status of the Rule. + * lock: A read/write lock on the structure. + * active: A read/write lock representing that something is currently using this (read locks = in use, write lock = see if in use and possibly prepare for delete). + * lock_attribute: The lock attribute for "lock". + * active_attribute: The lock attribute for "active". */ #ifndef _di_controller_process_t_ typedef struct { - f_string_dynamic_t id; + f_string_dynamic_t id_rule; f_status_t status; f_thread_lock_t lock; - f_thread_lock_attribute_t attribute; - f_thread_condition_t wait; + f_thread_lock_t active; + f_thread_lock_attribute_t lock_attribute; + f_thread_lock_attribute_t active_attribute; } controller_process_t; #define controller_process_t_initialize { \ f_string_dynamic_t_initialize \ F_known_not, \ f_thread_lock_t_initialize, \ + f_thread_lock_t_initialize, \ + f_thread_lock_attribute_t_initialize, \ f_thread_lock_attribute_t_initialize, \ - f_thread_condition_t_initialize, \ } - - #define controller_macro_process_t_clear(process) \ - process.status = F_known_not; \ - f_macro_string_dynamic_t_clear(process.id) #endif // _di_controller_process_t_ +/** + * The Rule Processes. + * + * array: An array of rule processes. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_processs_t_ typedef struct { controller_process_t *array; @@ -664,6 +763,16 @@ extern "C" { process.used = 0; #endif // _di_controller_processs_t_ +/** + * An Entry Item Action + * + * type: The type of Action. + * code: A single code or sub-type associated with the Action. + * line: The line number where the Entry Item begins. + * number: The unsigned number that some types use instead of the "parameters". + * status: The overall status. + * parameters: The values associated with the Action. + */ #ifndef _di_controller_entry_action_t_ enum { controller_entry_action_type_consider = 1, @@ -712,6 +821,13 @@ extern "C" { f_macro_string_dynamics_t_clear(action.parameters) #endif // _di_controller_entry_action_t_ +/** + * The Entry Item Actions. + * + * array: An array of Entry Item Actions. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_entry_actions_t_ typedef struct { controller_entry_action_t *array; @@ -732,6 +848,13 @@ extern "C" { actions.used = 0; #endif // _di_controller_entry_actions_t_ +/** + * An Entry Item. + * + * line: The line number where the Entry Item begins. + * name: The name of the Entry Item. + * actions: The Actions associated with the Entry Item. + */ #ifndef _di_controller_entry_item_t_ typedef struct { f_array_length_t line; @@ -753,6 +876,13 @@ extern "C" { controller_macro_entry_actions_t_clear(item.actions) #endif // _di_controller_entry_item_t_ +/** + * An Entry Items. + * + * array: An array of Entry Items. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_entry_items_t_ typedef struct { controller_entry_item_t *array; @@ -773,6 +903,12 @@ extern "C" { items.used = 0; #endif // _di_controller_entry_items_t_ +/** + * The Entry. + * + * status: The overall status. + * items: The array of entry items. + */ #ifndef _di_controller_entry_t_ typedef struct { f_status_t status; @@ -790,6 +926,22 @@ extern "C" { controller_macro_entry_items_t_clear(entry.items) #endif // _di_controller_entry_t_ +/** + * All setting data. + * + * interruptable: TRUE if the program responds to interrupt signals, FALSE to block/ignore interrupt signals. + * ready: State representing if the settings are all loaded and is ready to run program operations. + * timeout_kill: The timeout to wait relating to using a kill signal. + * timeout_start: The timeout to wait relating to starting a process. + * timeout_stop: The timeout to wait relating to stopping a process. + * failsafe_enabled: TRUE if failsafe execution is enabled, FALSE otherwise. + * failsafe_rule_id: The Rule ID (such as "failsafe/bash") to execute when failsafe execution is enabled. + * path_control: File path to the control socket. + * path_pid: File path to the PID file. + * path_setting: File path to the setting directory. + * entry: The entry settings. + * rules: All rules and their respective settings. + */ #ifndef _di_controller_setting_t enum { controller_setting_ready_no = 0, @@ -803,7 +955,6 @@ extern "C" { typedef struct { bool interruptable; uint8_t ready; - int signal; f_number_unsigned_t timeout_kill; f_number_unsigned_t timeout_start; @@ -823,7 +974,6 @@ extern "C" { #define controller_setting_t_initialize { \ F_false, \ 0, \ - 0, \ 3, \ 3, \ 3, \ @@ -839,7 +989,6 @@ extern "C" { #define controller_macro_setting_t_clear(setting) \ setting.interruptable = F_false; \ setting.ready = 0; \ - setting.signal = 0; \ setting.timeout_kill = 3; \ setting.timeout_start = 3; \ setting.timeout_stop = 3; \ @@ -852,6 +1001,16 @@ extern "C" { controller_macro_rules_t_clear(entry.setting) #endif // _di_controller_setting_t +/** + * Action related cache. + * + * line_action: The line in some file representing an Action. + * line_item: The line in some file representing an Item. + * name_action: A NULL terminated name of some Action. + * name_file: A NULL terminated name of some File. + * name_item: A NULL terminated name of some Item. + * generic: A NULL terminated string for general use. + */ #ifndef _di_controller_cache_action_t_ typedef struct { f_array_length_t line_action; @@ -882,6 +1041,25 @@ extern "C" { f_macro_string_dynamic_t_clear(cache.generic) #endif // _di_controller_cache_action_t_ +/** + * A cache intended for re-using memory while loading and processing rules whenever possible. + * + * timestamp: The timestamp. + * range_action: The Range for some Action. + * ats: Locations. + * stack: Locations within a items history used as a history stack for circular recursion prevention. + * comments: Comments associated with a buffer string. + * delimits: Delimits associated with a buffer string. + * content_action: The specific Content for some Action. + * content_actions: Content for some Action. + * content_items: Content for some Item. + * object_actions: Objects for some Action. + * object_items: Objects for some Item. + * buffer_file: A generic file related buffer. + * buffer_item: A generic item related buffer. + * buffer_path: A generic path related buffer. + * action: A cache for some Action, often used by error printing for reporting where an error happened. + */ #ifndef _di_controller_cache_t_ typedef struct { f_time_spec_t timestamp; @@ -924,26 +1102,25 @@ extern "C" { f_string_dynamic_t_initialize, \ controller_cache_action_t_initialize, \ } - - #define controller_macro_cache_t_clear(cache) \ - f_macro_time_spec_t_clear(cache.timestamp) \ - f_macro_string_range_t_clear(cache.range_action) \ - cache.ats = 0; \ - cache.stack = 0; \ - f_macro_time_spec_t_clear(cache.timestamp) \ - f_macro_fss_comments_t_clear(cache.comments) \ - f_macro_fss_delimits_t_clear(cache.delimits) \ - f_macro_fss_content_t_clear(cache.content_action) \ - f_macro_fss_contents_t_clear(cache.content_actions) \ - f_macro_fss_contents_t_clear(cache.content_items) \ - f_macro_fss_objects_t_clear(cache.object_actions) \ - f_macro_fss_objects_t_clear(cache.object_items) \ - f_macro_string_dynamic_t_clear(cache.buffer_file) \ - f_macro_string_dynamic_t_clear(cache.buffer_item) \ - f_macro_string_dynamic_t_clear(cache.buffer_path) \ - controller_macro_cache_action_t_clear(cache.action) #endif // _di_controller_cache_t_ +/** + * Stores data passed to a thread on thread creation, specifically for rule processing. + * + * The thread has different states: + * - active: The thread is running. + * - done: The thread has finished executed but has not been cleaned up. + * - joined: The thread has been cleaned up, this is safe to delete. + * + * id: The thread id, which is controlled by pthread functions. + * id_process: The id representing the rule process this asynchronous thread operates for. + * state: The state of the asynchronous. + * action: The action being performed. + * options: Configuration options for this asynchronous thread. + * child: The process id of a child process, if one is running (when forking to execute a child process). + * stack: The execution stack representing all other rule processes (by process id) that called this (to prevent infinite recursion). // @todo is this needed here anymore? + * cache: A per asynchronous thread cache. + */ #ifndef _di_controller_asynchronous_t_ enum { controller_asynchronous_state_active = 1, @@ -953,10 +1130,8 @@ extern "C" { typedef struct { f_thread_id_t id; - f_thread_lock_t lock; - f_thread_lock_attribute_t attribute; + f_array_length_t id_process; - f_array_length_t index; uint8_t state; uint8_t action; uint8_t options; @@ -968,8 +1143,6 @@ extern "C" { #define controller_asynchronous_t_initialize { \ f_thread_id_t_initialize, \ - f_thread_lock_t_initialize, \ - f_thread_lock_attribute_t_initialize, \ 0, \ 0, \ 0, \ @@ -978,19 +1151,15 @@ extern "C" { f_array_lengths_t_initialize, \ controller_cache_t_initialize \ } - - // @fixme remove the clear() macros..clear isn't safe when mixing in mutexes and whatnot that cannot be cleared. - #define controller_macro_asynchronous_t_clear(asynchronous) \ - f_macro_thread_id_t_clear(asynchronous.id) \ - asynchronous.index = 0; \ - asynchronous.state = 0; \ - asynchronous.action = 0; \ - asynchronous.options = 0; \ - asynchronous.child = 0; \ - f_macro_array_lengths_t_clear(asynchronous.stack) \ - controller_macro_cache_t_clear(asynchronous.cache) #endif // _di_controller_asynchronous_t_ +/** + * The asynchronous execution data. + * + * array: An array of asynchronous execution data. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ #ifndef _di_controller_asynchronouss_t_ typedef struct { controller_asynchronous_t *array; @@ -1011,9 +1180,26 @@ extern "C" { asynchronouss.used = 0; #endif // _di_controller_asynchronouss_t_ +/** + * A structure for managing threads. + * + * This is essentially data shared globally between threads, about threads. + * + * As a special case, index 0 of asynchronouss is reserved for use the main thread and is not used by any Rule Processes. + * + * enabled: TRUE when threads are active, FALSE when inactive and the program is essentially shutting down, no new threads should be started when FALSE. + * signal: The code of any signal received. + * id_cleanup: The thread ID representing the cleanup Process. + * id_control: The thread ID representing the cleanup Process. + * id_rule: The thread ID representing the cleanup Process. + * id_signal: The thread ID representing the cleanup Process. + * lock: A r/w lock for operating on this structure. + * asynchronouss: All Rule Process thread data. + */ #ifndef _di_controller_thread_t_ typedef struct { bool enabled; + int signal; f_thread_id_t id_cleanup; f_thread_id_t id_control; @@ -1026,6 +1212,7 @@ extern "C" { #define controller_thread_t_initialize { \ F_true, \ + 0, \ f_thread_id_t_initialize, \ f_thread_id_t_initialize, \ f_thread_id_t_initialize, \ @@ -1036,6 +1223,7 @@ extern "C" { #define controller_macro_thread_t_initialize(lock, asynchronouss) { \ F_true, \ + 0, \ f_thread_id_t_initialize, \ f_thread_id_t_initialize, \ f_thread_id_t_initialize, \ @@ -1046,6 +1234,7 @@ extern "C" { #define controller_macro_thread_t_clear(thread) \ thread.enabled = F_true; \ + thread.signal = 0; \ f_macro_thread_id_t_clear(thread.id_cleanup); \ f_macro_thread_id_t_clear(thread.id_control); \ f_macro_thread_id_t_clear(thread.id_rule); \ @@ -1053,6 +1242,15 @@ extern "C" { controller_macro_asynchronouss_t_clear(thread.asynchronouss) #endif // _di_controller_data_common_t_ +/** + * A wrapper used for passing all data to each individual asynchronous thread. + * + * id_process: The index in the processs array representing the current process. + * data: All standard program data. + * setting: All loaded settings. + * processs: All Rule Process data. + * thread: All thread related data. + */ #ifndef _di_controller_thread_data_t_ // @todo relocate these under a different ifdef block. #define controller_thread_cache_cleanup_interval_long 3600 // 1 hour in seconds. @@ -1063,7 +1261,7 @@ extern "C" { #define controller_thread_asynchronous_max 65535 // Total number of asynchronous threads allowed at any one time. typedef struct { - f_array_length_t id; + f_array_length_t id_process; controller_data_t *data; controller_setting_t *setting; @@ -1073,8 +1271,8 @@ extern "C" { #define controller_thread_data_t_initialize { 0, 0, 0, 0, 0 } - #define controller_macro_thread_data_t_initialize(id, data, setting, processs, thread) { \ - id, \ + #define controller_macro_thread_data_t_initialize(id_process, data, setting, processs, thread) { \ + id_process, \ data, \ setting, \ processs, \ @@ -1082,7 +1280,7 @@ extern "C" { } #define controller_macro_thread_data_t_clear(thread_data) \ - thread_data.id = 0; \ + thread_data.id_process = 0; \ thread_data.data = 0; \ thread_data.setting = 0; \ thread_data.processs = 0; \ diff --git a/level_3/controller/c/private-controller.c b/level_3/controller/c/private-controller.c index d188ced..24e82ca 100644 --- a/level_3/controller/c/private-controller.c +++ b/level_3/controller/c/private-controller.c @@ -259,7 +259,7 @@ extern "C" { for (f_array_length_t i = 0; i < processs.used; ++i) { - if (fl_string_dynamic_compare(id, processs.array[i].id) == F_equal_to) { + if (fl_string_dynamic_compare(id, processs.array[i].id_rule) == F_equal_to) { *at = i; return F_true; } @@ -451,7 +451,7 @@ extern "C" { for (; cache->ats.array[at_j] < actions->used; ++cache->ats.array[at_j]) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { return F_signal; } @@ -639,8 +639,8 @@ extern "C" { uint8_t rule_options = 0; - controller_entry_actions_t *actions = 0; - + controller_entry_action_t *entry_action = 0; + controller_entry_actions_t *entry_actions = 0; controller_process_t *process = 0; const bool simulate = thread_data.data->parameters[controller_parameter_test].result == f_console_result_found; @@ -697,19 +697,21 @@ extern "C" { // I would much rather gain the write lock and then release the read lock, but that is likely not possible. for (;;) { - actions = &thread_data.setting->entry.items.array[cache->ats.array[at_i]].actions; + entry_actions = &thread_data.setting->entry.items.array[cache->ats.array[at_i]].actions; - for (; cache->ats.array[at_j] < actions->used; ++cache->ats.array[at_j]) { + for (; cache->ats.array[at_j] < entry_actions->used; ++cache->ats.array[at_j]) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { status = F_signal; break; } - cache->action.line_action = actions->array[cache->ats.array[at_j]].line; + entry_action = &entry_actions->array[cache->ats.array[at_j]]; + + cache->action.line_action = entry_action->line; cache->action.name_action.used = 0; - status = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->action.name_action); + status = controller_string_dynamic_append_terminated(controller_entry_action_type_name(entry_action->type), &cache->action.name_action); if (F_status_is_error(status)) { controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread); @@ -717,9 +719,9 @@ extern "C" { return status; } - if (F_status_is_error(actions->array[cache->ats.array[at_j]].status)) { + if (F_status_is_error(entry_action->status)) { - if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) { + if (entry_action->type == controller_entry_action_type_rule) { if (simulate) { f_thread_mutex_lock(&thread_data.thread->lock.print); @@ -728,18 +730,18 @@ extern "C" { fprintf(thread_data.data->output.stream, "The entry item action '"); fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, cache->action.name_action.string, thread_data.data->context.set.title.after->string); - if (actions->array[cache->ats.array[at_j]].parameters.used) { + if (entry_action->parameters.used) { fprintf(thread_data.data->output.stream, f_string_space_s); fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.before->string); - controller_entry_action_parameters_print(thread_data.data->output.stream, actions->array[cache->ats.array[at_j]]); + controller_entry_action_parameters_print(thread_data.data->output.stream, entry_actions->array[cache->ats.array[at_j]]); fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.after->string); } - fprintf(thread_data.data->output.stream, "' is %s and is in a %sfailed%s state, skipping execution.%c", actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require ? "required" : "optional", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]); + fprintf(thread_data.data->output.stream, "' is %s and is in a %sfailed%s state, skipping execution.%c", entry_action->code & controller_entry_rule_code_require ? "required" : "optional", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]); f_thread_mutex_unlock(&thread_data.thread->lock.print); } - else if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) { + else if (entry_action->code & controller_entry_rule_code_require) { if (thread_data.data->error.verbosity != f_console_verbosity_quiet) { f_thread_mutex_lock(&thread_data.thread->lock.print); @@ -748,9 +750,9 @@ extern "C" { fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action '", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s); fprintf(thread_data.data->error.to.stream, "%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, cache->action.name_action.string); - if (actions->array[cache->ats.array[at_j]].parameters.used) { + if (entry_action->parameters.used) { fprintf(thread_data.data->error.to.stream, f_string_space_s); - controller_entry_action_parameters_print(thread_data.data->error.to.stream, actions->array[cache->ats.array[at_j]]); + controller_entry_action_parameters_print(thread_data.data->error.to.stream, entry_actions->array[cache->ats.array[at_j]]); } fprintf(thread_data.data->error.to.stream, "%s%s' is ", thread_data.data->error.notable.after->string, thread_data.data->error.context.before->string); @@ -773,9 +775,9 @@ extern "C" { fprintf(thread_data.data->warning.to.stream, "%s%sThe entry item action '", thread_data.data->warning.context.before->string, thread_data.data->warning.prefix ? thread_data.data->warning.prefix : f_string_empty_s); fprintf(thread_data.data->warning.to.stream, "%s%s%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, cache->action.name_action.string); - if (actions->array[cache->ats.array[at_j]].parameters.used) { + if (entry_action->parameters.used) { fprintf(thread_data.data->warning.to.stream, f_string_space_s); - controller_entry_action_parameters_print(thread_data.data->warning.to.stream, actions->array[cache->ats.array[at_j]]); + controller_entry_action_parameters_print(thread_data.data->warning.to.stream, entry_actions->array[cache->ats.array[at_j]]); } fprintf(thread_data.data->warning.to.stream, "%s%s' is ", thread_data.data->warning.notable.after->string, thread_data.data->warning.context.before->string); @@ -797,10 +799,10 @@ extern "C" { fprintf(thread_data.data->output.stream, "The entry item action '"); fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, cache->action.name_action.string, thread_data.data->context.set.title.after->string); - if (actions->array[cache->ats.array[at_j]].parameters.used) { + if (entry_action->parameters.used) { fprintf(thread_data.data->output.stream, f_string_space_s); fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.before->string); - controller_entry_action_parameters_print(thread_data.data->output.stream, actions->array[cache->ats.array[at_j]]); + controller_entry_action_parameters_print(thread_data.data->output.stream, entry_actions->array[cache->ats.array[at_j]]); fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.after->string); } @@ -815,9 +817,9 @@ extern "C" { fprintf(thread_data.data->warning.to.stream, "%s%sThe entry item action '", thread_data.data->warning.context.before->string, thread_data.data->warning.prefix ? thread_data.data->warning.prefix : f_string_empty_s); fprintf(thread_data.data->warning.to.stream, "%s%s", thread_data.data->warning.notable.before->string, cache->action.name_action.string); - if (actions->array[cache->ats.array[at_j]].parameters.used) { + if (entry_action->parameters.used) { fprintf(thread_data.data->warning.to.stream, f_string_space_s); - controller_entry_action_parameters_print(thread_data.data->warning.to.stream, actions->array[cache->ats.array[at_j]]); + controller_entry_action_parameters_print(thread_data.data->warning.to.stream, entry_actions->array[cache->ats.array[at_j]]); } fprintf(thread_data.data->warning.to.stream, "%s' is in a ", thread_data.data->warning.notable.after->string); @@ -833,7 +835,7 @@ extern "C" { continue; } - if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) { + if (entry_action->type == controller_entry_action_type_ready) { if (thread_data.setting->ready == controller_setting_ready_wait) { @@ -865,9 +867,9 @@ extern "C" { f_thread_mutex_unlock(&thread_data.thread->lock.print); } } - else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) { + else if (entry_action->type == controller_entry_action_type_item) { - if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].number >= thread_data.setting->entry.items.used) { + if (entry_action->number == 0 || entry_action->number >= thread_data.setting->entry.items.used) { // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors. if (thread_data.data->error.verbosity != f_console_verbosity_quiet) { @@ -875,7 +877,7 @@ extern "C" { fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]); fprintf(thread_data.data->error.to.stream, "%s%sInvalid entry item index ", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s); - fprintf(thread_data.data->error.to.stream, "%s%s%llu%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, actions->array[cache->ats.array[at_j]].number, thread_data.data->error.notable.after->string); + fprintf(thread_data.data->error.to.stream, "%s%s%llu%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, entry_action->number, thread_data.data->error.notable.after->string); fprintf(thread_data.data->error.to.stream, "%s detected.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]); controller_entry_error_print_cache(thread_data.data->error, cache->action); @@ -895,7 +897,7 @@ extern "C" { } // continue into the requested item. - cache->ats.array[cache->ats.used] = actions->array[cache->ats.array[at_j]].number; + cache->ats.array[cache->ats.used] = entry_action->number; cache->ats.array[cache->ats.used + 1] = 0; at_i = cache->ats.used; @@ -931,7 +933,7 @@ extern "C" { // exit inner loop to force restarting and start processing the requested item. break; } - else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_consider || actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) { + else if (entry_action->type == controller_entry_action_type_consider || entry_action->type == controller_entry_action_type_rule) { status = controller_rules_increase(&thread_data.setting->rules); @@ -941,37 +943,35 @@ extern "C" { return status; } - const f_array_length_t rule_id_length = actions->array[cache->ats.array[at_j]].parameters.array[0].used + actions->array[cache->ats.array[at_j]].parameters.array[1].used + 1; - char rule_id_name[rule_id_length + 1]; - const f_string_static_t rule_id = f_macro_string_static_t_initialize(rule_id_name, rule_id_length); + const f_array_length_t id_rule_length = entry_action->parameters.array[0].used + entry_action->parameters.array[1].used + 1; + char id_rule_name[id_rule_length + 1]; + const f_string_static_t id_rule = f_macro_string_static_t_initialize(id_rule_name, id_rule_length); - memcpy(rule_id_name, actions->array[cache->ats.array[at_j]].parameters.array[0].string, actions->array[cache->ats.array[at_j]].parameters.array[0].used); - memcpy(rule_id_name + actions->array[cache->ats.array[at_j]].parameters.array[0].used + 1, actions->array[cache->ats.array[at_j]].parameters.array[1].string, actions->array[cache->ats.array[at_j]].parameters.array[1].used); + memcpy(id_rule_name, entry_action->parameters.array[0].string, entry_action->parameters.array[0].used); + memcpy(id_rule_name + entry_action->parameters.array[0].used + 1, entry_action->parameters.array[1].string, entry_action->parameters.array[1].used); - rule_id_name[actions->array[cache->ats.array[at_j]].parameters.array[0].used] = f_path_separator_s[0]; - rule_id_name[rule_id_length] = 0; + id_rule_name[entry_action->parameters.array[0].used] = f_path_separator_s[0]; + id_rule_name[id_rule_length] = 0; f_thread_lock_read(&thread_data.thread->lock.rule); - at = controller_rule_find_loaded(rule_id, thread_data); - - const f_array_length_t used = thread_data.setting->rules.used; - - f_thread_unlock(&thread_data.thread->lock.rule); + at = controller_rule_find_loaded(id_rule, thread_data); if (simulate) { f_thread_mutex_lock(&thread_data.thread->lock.print); fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]); - fprintf(thread_data.data->output.stream, "%s entry item rule '", actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule ? "Processing" : "Considering"); - fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, rule_id.string, thread_data.data->context.set.title.after->string); + fprintf(thread_data.data->output.stream, "%s entry item rule '", entry_action->type == controller_entry_action_type_rule ? "Processing" : "Considering"); + fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, id_rule.string, thread_data.data->context.set.title.after->string); fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]); f_thread_mutex_unlock(&thread_data.thread->lock.print); } // the rule is not yet loaded, ensure that it is loaded. - if (at == used) { + if (at == thread_data.setting->rules.used) { + + f_thread_unlock(&thread_data.thread->lock.rule); // rule execution will re-use the existing cache, so save the current cache. const f_array_length_t cache_line_action = cache->action.line_action; @@ -991,7 +991,7 @@ extern "C" { f_thread_lock_write(&thread_data.thread->lock.rule); - status = controller_rule_read(rule_id, thread_data, cache, &thread_data.setting->rules.array[thread_data.setting->rules.used]); + status = controller_rule_read(id_rule, thread_data, cache, &thread_data.setting->rules.array[thread_data.setting->rules.used]); // restore cache. memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used); @@ -1032,7 +1032,7 @@ extern "C" { f_thread_unlock(&thread_data.thread->lock.rule); } - if (F_status_is_error_not(status) && actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) { + if (F_status_is_error_not(status) && entry_action->type == controller_entry_action_type_rule) { // rule execution will re-use the existing cache, so save the current cache. const f_array_length_t cache_line_action = cache->action.line_action; @@ -1056,56 +1056,23 @@ extern "C" { rule_options |= controller_rule_option_simulate; } - if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) { + if (entry_action->code & controller_entry_rule_code_require) { rule_options |= controller_rule_option_require; } - if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_wait) { + if (entry_action->code & controller_entry_rule_code_wait) { rule_options |= controller_rule_option_wait; } - if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_asynchronous) { + if (entry_action->code & controller_entry_rule_code_asynchronous) { rule_options |= controller_rule_option_asynchronous; - // @todo this function will need to do the same process prep work as is done right before controller_rule_process(). - status = controller_rule_process_asynchronous(at, controller_rule_action_type_start, rule_options, thread_data, cache); + // @todo + //status = controller_rule_process_asynchronous(id_rule, controller_rule_action_type_start, rule_options, thread_data, cache); } else { - - f_array_length_t at_process = 0; - - f_thread_lock_read(&thread_data.thread->lock.process); - - if (controller_find_process(rule_id, *thread_data.processs, &at_process) == F_false) { - f_thread_unlock(&thread_data.thread->lock.process); - f_thread_lock_write(&thread_data.thread->lock.process); - - status = controller_processs_increase(thread_data.processs); - - if (F_status_is_error(status)) { - controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_processs_increase", F_true, thread_data.thread); - } - else { - at_process = thread_data.processs->used; - } - } - - f_thread_unlock(&thread_data.thread->lock.process); - - if (F_status_is_error_not(status)) { - f_thread_lock_write(&thread_data.processs->array[at_process].lock); - - controller_rule_t rule = controller_rule_t_initialize; - - status = controller_rule_copy(thread_data.setting->rules.array[at], &rule); - - f_thread_condition_signal(&thread_data.processs->array[at_process].wait); - f_thread_unlock(&thread_data.processs->array[at_process].lock); - - status = controller_rule_process(rule, at_process, controller_rule_action_type_start, rule_options, thread_data, cache); - - controller_rule_delete_simple(&rule); - } + // @todo + // status = controller_rule_process(id_rule, controller_rule_action_type_start, rule_options, thread_data, cache); } // restore cache. @@ -1124,34 +1091,23 @@ extern "C" { cache->action.line_action = cache_line_action; cache->action.line_item = cache_line_item; - if (status == F_child || status == F_signal) break; - } - - // @todo this may need to be relocated (currently it may result in multiple error messages due to other messages being printed above.) - if (F_status_is_error(status)) { - f_thread_mutex_lock(&thread_data.thread->lock.print); - - controller_entry_error_print_cache(thread_data.data->error, cache->action); - - f_thread_mutex_unlock(&thread_data.thread->lock.print); - - if (!simulate || F_status_set_fine(status) == F_memory_not) { + if (!simulate || F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal) { break; } } } - else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_timeout) { + else if (entry_action->type == controller_entry_action_type_timeout) { if (simulate) { f_string_t code = ""; - if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_kill) { + if (entry_action->code == controller_entry_timeout_code_kill) { code = controller_string_kill_s; } - else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_start) { + else if (entry_action->code == controller_entry_timeout_code_start) { code = controller_string_start_s; } - else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_stop) { + else if (entry_action->code == controller_entry_timeout_code_stop) { code = controller_string_stop_s; } @@ -1163,25 +1119,25 @@ extern "C" { fprintf(thread_data.data->output.stream, "' setting '"); fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.important.before->string, code, thread_data.data->context.set.important.after->string); fprintf(thread_data.data->output.stream, "' to '"); - fprintf(thread_data.data->output.stream, "%s%llu%s", thread_data.data->context.set.important.before->string, actions->array[cache->ats.array[at_j]].number, thread_data.data->context.set.important.after->string); + fprintf(thread_data.data->output.stream, "%s%llu%s", thread_data.data->context.set.important.before->string, entry_action->number, thread_data.data->context.set.important.after->string); fprintf(thread_data.data->output.stream, "' MegaTime (milliseconds).%c", f_string_eol_s[0]); f_thread_mutex_unlock(&thread_data.thread->lock.print); } - if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_kill) { - thread_data.setting->timeout_kill = actions->array[cache->ats.array[at_j]].number; + if (entry_action->code == controller_entry_timeout_code_kill) { + thread_data.setting->timeout_kill = entry_action->number; } - else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_start) { - thread_data.setting->timeout_start = actions->array[cache->ats.array[at_j]].number; + else if (entry_action->code == controller_entry_timeout_code_start) { + thread_data.setting->timeout_start = entry_action->number; } - else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_stop) { - thread_data.setting->timeout_stop = actions->array[cache->ats.array[at_j]].number; + else if (entry_action->code == controller_entry_timeout_code_stop) { + thread_data.setting->timeout_stop = entry_action->number; } } - else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_failsafe) { + else if (entry_action->type == controller_entry_action_type_failsafe) { - if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].number >= thread_data.setting->entry.items.used) { + if (entry_action->number == 0 || entry_action->number >= thread_data.setting->entry.items.used) { // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors. if (thread_data.data->error.verbosity != f_console_verbosity_quiet) { @@ -1189,7 +1145,7 @@ extern "C" { fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]); fprintf(thread_data.data->error.to.stream, "%s%sInvalid entry item index ", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s); - fprintf(thread_data.data->error.to.stream, "%s%s%llu%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, actions->array[cache->ats.array[at_j]].number, thread_data.data->error.notable.after->string); + fprintf(thread_data.data->error.to.stream, "%s%s%llu%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, entry_action->number, thread_data.data->error.notable.after->string); fprintf(thread_data.data->error.to.stream, "%s detected.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]); controller_entry_error_print_cache(thread_data.data->error, cache->action); @@ -1201,7 +1157,7 @@ extern "C" { } else { thread_data.setting->failsafe_enabled = F_true; - thread_data.setting->failsafe_rule_id = actions->array[cache->ats.array[at_j]].number; + thread_data.setting->failsafe_rule_id = entry_action->number; if (simulate) { f_thread_mutex_lock(&thread_data.thread->lock.print); @@ -1219,7 +1175,7 @@ extern "C" { } } // for - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { status = F_signal; } @@ -1235,7 +1191,7 @@ extern "C" { } // end of actions found, so drop to previous loop in stack. - if (cache->ats.array[at_j] == actions->used) { + if (cache->ats.array[at_j] == entry_actions->used) { // all actions for "main" are processed so there is nothing left to do. if (at_i == 0) break; diff --git a/level_3/controller/c/private-entry.c b/level_3/controller/c/private-entry.c index d2f5247..7f42a22 100644 --- a/level_3/controller/c/private-entry.c +++ b/level_3/controller/c/private-entry.c @@ -702,7 +702,7 @@ extern "C" { for (; i < cache->object_items.used; ++i) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { return F_signal; } @@ -853,7 +853,7 @@ extern "C" { for (j = 0; j < thread_data.setting->entry.items.array[i].actions.used; ++j) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { return F_signal; } diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 3dfdc0f..7c8e729 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -770,7 +770,7 @@ extern "C" { for (i = 0; i < rule->items.used; ++i) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { status = F_signal; break; } @@ -779,7 +779,7 @@ extern "C" { for (j = 0; j < rule->items.array[i].actions.used; ++j) { - if (thread_data.setting->signal) { + if (thread_data.thread->signal) { status = F_signal; break; } @@ -1109,6 +1109,7 @@ extern "C" { } #endif // _di_controller_rule_execute_pid_with_ +// @todo consider changing this to accept "at" as an argument and returning status so that error status can be returned. #ifndef _di_controller_rule_find_loaded_ f_array_length_t controller_rule_find_loaded(const f_string_static_t rule_id, controller_thread_data_t thread_data) { @@ -1985,20 +1986,77 @@ extern "C" { } #endif // _di_controller_rule_process_ +#ifndef _di_controller_rule_process_do_ + f_status_t controller_rule_process_do(const controller_rule_t rule, const f_array_length_t at_process, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) { + + f_array_length_t at_process = 0; + + f_thread_lock_read(&thread_data.thread->lock.process); + + if (controller_find_process(rule_id, *thread_data.processs, &at_process) == F_false) { + f_thread_unlock(&thread_data.thread->lock.process); + f_thread_lock_write(&thread_data.thread->lock.process); + + status = controller_processs_increase(thread_data.processs); + + if (F_status_is_error(status)) { + controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_processs_increase", F_true, thread_data.thread); + } + else { + at_process = thread_data.processs->used; + } + } + + f_thread_unlock(&thread_data.thread->lock.process); + + if (F_status_is_error_not(status)) { + + // retrieve a copy of the rule and use that for the lifespan of the rule's execution within the designated process. + controller_rule_t rule = controller_rule_t_initialize; + + status = controller_rule_copy(thread_data.setting->rules.array[at], &rule); + + f_thread_unlock(&thread_data.thread->lock.rule); + + if (F_status_is_error(status)) { + controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_rule_copy", F_true, thread_data.thread); + } + else { + status = controller_rule_process(rule, at_process, controller_rule_action_type_start, rule_options, thread_data, cache); + + controller_rule_delete_simple(&rule); + + if (F_status_is_error(status)) { + f_thread_mutex_lock(&thread_data.thread->lock.print); + + controller_entry_error_print_cache(thread_data.data->error, cache->action); + + f_thread_mutex_unlock(&thread_data.thread->lock.print); + } + } + } + } +#endif // _di_controller_rule_process_do_ + #ifndef _di_controller_rule_process_asynchronous_ - f_status_t controller_rule_process_asynchronous(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) { + f_status_t controller_rule_process_asynchronous(const f_string_static_t id_rule, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) { - f_thread_mutex_lock(&thread_data->lock.asynchronous); + f_thread_lock_read(&thread_data.lock.asynchronous); if (!thread_data.thread->enabled) { - f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous); + f_thread_mutex_unlock(&thread_data.lock.asynchronous); return F_signal; } + f_thread_mutex_unlock(&thread_data.lock.asynchronous); + f_thread_lock_write(&thread_data.lock.asynchronous); + f_status_t status = controller_asynchronouss_increase(&thread_data.thread->asynchronouss); if (F_status_is_error(status)) { + controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_asynchronouss_increase", F_true, thread_data.thread); + f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous); return status; @@ -2006,37 +2064,89 @@ extern "C" { controller_asynchronous_t *asynchronous = &thread_data.thread->asynchronouss.array[thread_data.thread->asynchronouss.used++]; - controller_macro_asynchronous_t_clear((*asynchronous)); + f_thread_lock_read(&thread_data.lock.process); - asynchronous->index = index; - asynchronous->state = controller_asynchronous_state_active; - asynchronous->action = action; - asynchronous->options = options; - asynchronous->thread_data.thread = (void *) thread_data.thread; - asynchronous->cache.line_action = thread_data.thread->cache_action->line_action; - asynchronous->cache.line_item = thread_data.thread->cache_action->line_item; + if (controller_find_process(id_rule, *thread_data.processs, &asynchronous->id_process) == F_true) { - status = f_string_dynamic_append(thread_data.thread->cache_action->name_action, &asynchronous->cache.name_action); - - if (F_status_is_error_not(status)) { - status = f_string_dynamic_append(thread_data.thread->cache_action->name_file, &asynchronous->cache.name_file); + // use read locks to designate that the process is in use. + f_thread_lock_read(&thread_data.processs->array[asynchronous->id_process].lock); } + else { - if (F_status_is_error_not(status)) { - status = f_string_dynamic_append(thread_data.thread->cache_action->name_item, &asynchronous->cache.name_item); + status = F_status_set_error(F_failure); + + if (thread_data.data->error.verbosity != f_console_verbosity_quiet) { + f_thread_mutex_lock(&thread_main->thread->lock.print); + + fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]); + fprintf(thread_data.data->output.stream, "The entry item rule '"); + fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, id_rule.string, thread_data.data->context.set.title.after->string); + fprintf(thread_data.data->output.stream, "' is no longer loaded.%c", f_string_eol_s[0]); + + controller_entry_error_print_cache(thread_data.data->error, cache->action); + + f_thread_mutex_unlock(&thread_data.thread->lock.print); + } } + f_thread_unlock(&thread_data.lock.process); + if (F_status_is_error_not(status)) { - status = f_thread_create(0, &asynchronous->id, controller_thread_asynchronous, (void *) asynchronous); + + asynchronous->state = controller_asynchronous_state_active; + asynchronous->action = action; + asynchronous->options = options; + asynchronous->thread_data.thread = (void *) thread_data.thread; + //asynchronous->stack.used = 0; // @todo stack should be passed along each call!, might be better to make this a pointer. Maybe make this a id of process id? + + f_macro_time_spec_t_clear(asynchronous->cache.timestamp) + f_macro_string_range_t_clear(asynchronous->cache.range_action) + + asynchronous->cache.ats.used = 0; + asynchronous->cache.stack.used = 0; + asynchronous->cache.comments.used = 0; + asynchronous->cache.delimits.used = 0; + asynchronous->cache.content_action.used = 0; + asynchronous->cache.content_actions.used = 0; + asynchronous->cache.content_items.used = 0; + asynchronous->cache.object_actions.used = 0; + asynchronous->cache.object_items.used = 0; + asynchronous->cache.buffer_file.used = 0; + asynchronous->cache.buffer_item.used = 0; + asynchronous->cache.buffer_path.used = 0; + asynchronous->cache.action.used = 0; + asynchronous->cache.line_action = thread_data.thread->cache_action->line_action; + asynchronous->cache.line_item = thread_data.thread->cache_action->line_item; + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(thread_data.thread->cache_action->name_action, &asynchronous->cache.name_action); + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(thread_data.thread->cache_action->name_file, &asynchronous->cache.name_file); + } + + if (F_status_is_error_not(status)) { + status = f_string_dynamic_append(thread_data.thread->cache_action->name_item, &asynchronous->cache.name_item); + } + else { + controller_entry_error_print(thread_data.data->error, asynchronous->cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, thread_data.thread); + } + } + + if (F_status_is_error_not(status)) { + status = f_thread_create(0, &asynchronous->id, controller_thread_asynchronous_process, (void *) asynchronous); + } } if (F_status_is_error(status)) { - controller_macro_asynchronous_t_delete_simple((*asynchronous)); + controller_entry_error_print(thread_data.data->error, asynchronous->cache->action, F_status_set_fine(status), "f_thread_create", F_true, thread_data.thread); + + controller_asynchronous_delete_simple(asynchronous); thread_data.thread->asynchronouss.used--; } - f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous); + f_thread_unlock(&thread_data->lock.asynchronous); if (F_status_is_error(status)) { return status; diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index be56f00..76b203b 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -593,8 +593,8 @@ extern "C" { * * This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_array_lengths_t array. * - * @param index - * Position in the rules array representing the rule to execute + * @param id_rule + * The ID of the rule, such as "boot/init". * @param action * The action to perform based on the action type codes. * @@ -623,7 +623,7 @@ extern "C" { * Errors (with error bit) from: f_thread_create(). */ #ifndef _di_controller_rule_process_asynchronous_ - extern f_status_t controller_rule_process_asynchronous(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal; + extern f_status_t controller_rule_process_asynchronous(const f_string_static_t id_rule, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal; #endif // _di_controller_rule_process_asynchronous_ /** diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index 53eefb6..e9be560 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -9,40 +9,73 @@ extern "C" { #endif -#ifndef _di_controller_thread_asynchronous_ - void * controller_thread_asynchronous(void *arguments) { +#ifndef _di_controller_thread_asynchronous_process_ + void * controller_thread_asynchronous_process(void *arguments) { controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) arguments; - controller_thread_t *thread_main = (controller_thread_t *) asynchronous->thread; + controller_thread_t *main = (controller_thread_t *) asynchronous->thread; + + f_thread_lock_read(&main->lock.asynchronous); + + if (!main->enabled) { + f_thread_unlock(&main->lock.asynchronous); - if (!thread_main->enabled) { return 0; } - controller_thread_t thread = controller_thread_t_initialize; + f_thread_unlock(&main->lock.asynchronous); - f_thread_mutex_lock(&thread_main->setting->rules.array[asynchronous->index].lock); - f_thread_mutex_lock(&asynchronous->lock); + f_array_length_t at_process = 0; - thread.cache_main = thread_main->cache_main; - thread.cache_action = &asynchronous->cache; - thread.data = thread_main->data; - thread.lock = thread_main->lock; - thread.setting = thread_main->setting; - thread.stack = &asynchronous->stack; + f_thread_lock_read(&main->thread->lock.process); - if (controller_rule_process(asynchronous->index, asynchronous->action, asynchronous->options, &thread, asynchronous) != F_child) { - asynchronous->state = controller_asynchronous_state_done; + if (controller_find_process(rule.id, *main->processs, &at_process) == F_false) { + f_thread_unlock(&main->thread->lock.process); + f_thread_lock_write(&main->thread->lock.process); + + const f_status_t status = controller_processs_increase(main->processs); + + if (F_status_is_error(status)) { + controller_entry_error_print(main->data->error, asynchronous->cache->action, F_status_set_fine(status), "controller_processs_increase", F_true, main->thread); + + f_thread_unlock(&main->thread->lock.process); + return 0; + } + else { + at_process = main->processs->used++; + } } - f_thread_mutex_unlock(&asynchronous->lock); + // once the "active" lock is in place for some process, then it will not be deleted and should be guaranteed to not change or be relocated. + f_thread_lock_read(&main->processs.array[id_process].active); + f_thread_unlock(&main->thread->lock.process); + + // @todo looks like I do need a r/w lock on controller_asynchronous_t after all. - f_thread_condition_signal(&thread_main->setting->rules.array[asynchronous->index].wait); - f_thread_mutex_unlock(&thread_main->setting->rules.array[asynchronous->index].lock); + controller_rule_t rule = controller_rule_t_initialize; + + // @todo copy rule, finding the rule using main->processs.array[id_process].id_rule. + //status = controller_rule_copy(, &rule); + + { + const f_status_t status = controller_rule_process(rule, at_process, controller_rule_action_type_start, rule_options, thread_data, asynchronous->cache); + + asynchronous->state = controller_asynchronous_state_done; + + if (F_status_is_error(status)) { + f_thread_mutex_lock(&thread_data.thread->lock.print); + + controller_entry_error_print_cache(thread_data.data->error, asynchronous->cache->action); + + f_thread_mutex_unlock(&thread_data.thread->lock.print); + } + } + + f_thread_unlock(&main->processs.array[id_process].active); return 0; } -#endif // _di_controller_thread_asynchronous_ +#endif // _di_controller_thread_asynchronous_process_ #ifndef _di_controller_thread_asynchronous_cancel_ void controller_thread_asynchronous_cancel(controller_thread_t *thread) { @@ -174,12 +207,12 @@ extern "C" { controller_thread_t thread = controller_thread_t_initialize; controller_processs_t processs = controller_processs_t_initialize; - controller_thread_data_t data_main = controller_macro_thread_data_t_initialize(0, data, setting, &processs, &thread); + controller_thread_data_t thread_data = controller_macro_thread_data_t_initialize(0, data, setting, &processs, &thread); status = controller_asynchronouss_increase(&thread.asynchronouss); if (F_status_is_error_not(status)) { - status = f_thread_create(0, &thread.id_signal, &controller_thread_signal, (void *) &data_main); + status = f_thread_create(0, &thread.id_signal, &controller_thread_signal, (void *) &thread_data); } if (F_status_is_error(status)) { @@ -210,13 +243,16 @@ extern "C" { } else { - status = controller_entry_read(entry_name, data_main, &thread.asynchronouss.array[0].cache); + // index 0 is reserved for running the main thread cache. + thread.asynchronouss.used = 1; + + status = controller_entry_read(entry_name, thread_data, &thread.asynchronouss.array[0].cache); if (F_status_is_error(status)) { setting->ready = controller_setting_ready_fail; } else if (status != F_signal && status != F_child) { - status = controller_preprocess_entry(data_main, &thread.asynchronouss.array[0].cache); + status = controller_preprocess_entry(thread_data, &thread.asynchronouss.array[0].cache); } if (F_status_is_error_not(status) && status != F_signal && status != F_child) { @@ -238,7 +274,7 @@ extern "C" { status = F_status_set_error(F_available_not); } else { - status = controller_process_entry(data_main, &thread.asynchronouss.array[0].cache); + status = controller_process_entry(thread_data, &thread.asynchronouss.array[0].cache); if (F_status_is_error(status)) { setting->ready = controller_setting_ready_fail; @@ -271,14 +307,14 @@ extern "C" { if (data->parameters[controller_parameter_validate].result == f_console_result_none) { controller_rule_wait_all(&thread); - status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &data_main); + status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &thread_data); if (F_status_is_error_not(status)) { - status = f_thread_create(0, &thread.id_control, &controller_thread_control, (void *) &data_main); + status = f_thread_create(0, &thread.id_control, &controller_thread_control, (void *) &thread_data); } if (F_status_is_error_not(status)) { - status = f_thread_create(0, &thread.id_cleanup, &controller_thread_cleanup, (void *) &data_main); + status = f_thread_create(0, &thread.id_cleanup, &controller_thread_cleanup, (void *) &thread_data); } if (F_status_is_error(status)) { @@ -358,7 +394,7 @@ extern "C" { if (thread_data->data->parameters[controller_parameter_interruptable].result == f_console_result_found) { if (signal == F_signal_interrupt || signal == F_signal_abort || signal == F_signal_quit || signal == F_signal_termination) { - thread_data->setting->signal = signal; + thread_data->thread->signal = signal; controller_thread_asynchronous_cancel(thread_data->thread); break; diff --git a/level_3/controller/c/private-thread.h b/level_3/controller/c/private-thread.h index 99e7f80..22051b3 100644 --- a/level_3/controller/c/private-thread.h +++ b/level_3/controller/c/private-thread.h @@ -22,9 +22,9 @@ extern "C" { * @return * 0, always. */ -#ifndef _di_controller_thread_asynchronous_ - extern void * controller_thread_asynchronous(void *arguments) f_gcc_attribute_visibility_internal; -#endif // _di_controller_thread_asynchronous_ +#ifndef _di_controller_thread_asynchronous_process_ + extern void * controller_thread_asynchronous_process(void *arguments) f_gcc_attribute_visibility_internal; +#endif // _di_controller_thread_asynchronous_process_ /** * Cancel all asynchronous threads. -- 1.8.3.1