]> Kevux Git Server - controller/commitdiff
Progress: Continue migrating the project.
authorKevin Day <kevin@kevux.org>
Thu, 2 May 2024 01:11:31 +0000 (20:11 -0500)
committerKevin Day <kevin@kevux.org>
Thu, 2 May 2024 01:11:31 +0000 (20:11 -0500)
21 files changed:
data/build/dependencies
data/build/settings
sources/c/main/common/string.c
sources/c/main/common/string.h
sources/c/main/common/type/global.h
sources/c/main/common/type/lock.h
sources/c/main/controller.h
sources/c/main/instance.c [new file with mode: 0644]
sources/c/main/instance.h [new file with mode: 0644]
sources/c/main/path.c
sources/c/main/rule.c [new file with mode: 0644]
sources/c/main/rule.h [new file with mode: 0644]
sources/c/main/rule/instance.c [new file with mode: 0644]
sources/c/main/rule/instance.h [new file with mode: 0644]
sources/c/main/thread.c
sources/c/main/thread.h
sources/c/main/thread/instance.c
sources/c/main/thread/is.c
sources/c/main/thread/is.h
sources/c/main/time.c
sources/c/main/time.h

index ca0cecf35b4a378dd18e351d36066fda9d9a6272..37668f9db3f376ac19ca9a66a54afed788b77202 100644 (file)
@@ -28,6 +28,7 @@ f_rip
 f_signal
 f_socket
 f_status_string
+f_time
 f_thread
 
 fl_control_group
index bed0aab0920f3b451c441be978cbf7d5445f34fd..6dd2c98b1b68af7ab456cd5b3aec34c2b1b18d14 100644 (file)
@@ -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
 
index ed8527229aa7690160d8568aad4080952ad7e98e..6c32c05d42dec149e5a3d26ee9f47463f9617024 100644 (file)
@@ -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
index 72f1d19aea623d8ea2704aa183655caa46c41135..a335bd52156e09c56e0ba4cef16c34881a5ad8f1 100644 (file)
@@ -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
index ad98ec256be65d660f5c50cbeb1ae3f79f017f37..c770b0eb7429110735c386ba13790fa2d1117d58 100644 (file)
@@ -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 {
index 6e1b291cb4d33a7e0b542a0f48508de14286b5c1..56fb5a2841632800d0cef96365f8fc99f2bdaab7 100644 (file)
 #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;
index 2743bcd388010e0461398245255fbc15a0495614..5d269a8c534a99f30f445137f41f564f17cc5776 100644 (file)
@@ -44,6 +44,7 @@
 #include <fll/level_0/rip.h>
 #include <fll/level_0/signal.h>
 #include <fll/level_0/socket.h>
+#include <fll/level_0/time.h>
 
 #ifndef _di_thread_support_
   #include <fll/level_0/thread.h>
 #include <program/controller/main/print/verbose.h>
 #include <program/controller/main/print/warning.h>
 #include <program/controller/main/signal.h>
+#include <program/controller/main/time.h>
 #include <program/controller/main/thread/is.h>
 #include <program/controller/main/thread/instance.h>
 #include <program/controller/main/thread.h>
+#include <program/controller/main/instance.h>
+#include <program/controller/main/rule.h>
+#include <program/controller/main/rule/instance.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/sources/c/main/instance.c b/sources/c/main/instance.c
new file mode 100644 (file)
index 0000000..b4400f9
--- /dev/null
@@ -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 (file)
index 0000000..8916aa5
--- /dev/null
@@ -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
index d1133673ca4ea3fc86d0b61822ee1ae44e71c036..e3dd8e7127fe3ad05130fc683a2efecd365f84f5 100644 (file)
@@ -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 (file)
index 0000000..b4400f9
--- /dev/null
@@ -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 (file)
index 0000000..db45e42
--- /dev/null
@@ -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 (file)
index 0000000..884baad
--- /dev/null
@@ -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 (file)
index 0000000..f77e8b6
--- /dev/null
@@ -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
index 29c20c072461882b414bbe3d1ea975e3cd290b12..4a69ceca8a8f163c9b4a6190d13bd39f8a5d07eb 100644 (file)
@@ -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);
 
index 3290be09a124f4725805c26d6aac63c86dc02cbb..5856483919b6738e426b6901ac04a2ea60a6128b 100644 (file)
@@ -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
  * @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
index 3da4a862f84d31a16abca5dfdf1c7b363fc9ff5b..5ae10d84191281f0c52bb322ed659a943fae3619 100644 (file)
@@ -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;
index bfa4d091847306b0c9251fc7571f997a79874516..02a594f8947a863ebe36a5ad025d56d723d8ac91 100644 (file)
@@ -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"
index 3a07baa19c966259977759e9a02758a36646cc2b..f7fff4c270bae238bb8870985f05e83eec1cdd4d 100644 (file)
@@ -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"
index 99b7205ac3428bfffa3c29f408e861dfcbba715d..19ee1cdd6aa58c28814151c8a1083f534932cb14 100644 (file)
@@ -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_
 
index 644e9bbd4aabd0679dd179c402ce7fec1d0986d7..651b6bcbf71e093df756a9e0cd8fcfd4fd7ae927 100644 (file)
@@ -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().
  *