]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Sat, 6 Mar 2021 23:01:25 +0000 (17:01 -0600)
committerKevin Day <thekevinday@gmail.com>
Sat, 6 Mar 2021 23:01:25 +0000 (17:01 -0600)
Continue working on the thread support.

There are considerable redesigns being implemented.
The code is currently in an inconsistent state.

12 files changed:
level_3/controller/c/controller.c
level_3/controller/c/controller.h
level_3/controller/c/private-common.c
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-controller.h
level_3/controller/c/private-entry.c
level_3/controller/c/private-entry.h
level_3/controller/c/private-rule.c
level_3/controller/c/private-rule.h
level_3/controller/c/private-thread.c
level_3/controller/c/private-thread.h

index 026be3404145540ce5cef74a8f2ec8dd85454b7e..31258991d08a24ad26dd51a753128656f56e883b 100644 (file)
@@ -145,15 +145,12 @@ extern "C" {
     }
 
     controller_setting_t setting = controller_setting_t_initialize;
-    controller_cache_t cache = controller_cache_t_initialize;
-    controller_mutex_t mutex = controller_mutex_t_initialize;
-    controller_thread_t thread = controller_macro_thread_t_initialize(&cache, &cache.action, data, &mutex, &setting, &cache.stack);
 
     f_string_static_t entry_name = f_string_static_t_initialize;
 
     if (data->remaining.used) {
       entry_name.string = arguments.argv[data->remaining.array[0]];
-      entry_name.used = strnlen(entry_name.string, f_console_length_size);
+      entry_name.used = strnlen(entry_name.string, f_console_parameter_size);
       entry_name.size = entry_name.used;
     }
     else {
@@ -206,7 +203,7 @@ extern "C" {
     else if (data->parameters[controller_parameter_pid].locations.used) {
       const f_array_length_t location = data->parameters[controller_parameter_pid].values.array[data->parameters[controller_parameter_pid].values.used - 1];
 
-      if (strnlen(arguments.argv[location], f_console_length_size)) {
+      if (strnlen(arguments.argv[location], f_console_parameter_size)) {
         status = fll_path_canonical(arguments.argv[location], &setting.path_pid);
 
         if (F_status_is_error(status)) {
@@ -257,7 +254,7 @@ extern "C" {
     else if (data->parameters[controller_parameter_control].locations.used) {
       const f_array_length_t location = data->parameters[controller_parameter_control].values.array[data->parameters[controller_parameter_control].values.used - 1];
 
-      if (strnlen(arguments.argv[location], f_console_length_size)) {
+      if (strnlen(arguments.argv[location], f_console_parameter_size)) {
         status = fll_path_canonical(arguments.argv[location], &setting.path_control);
 
         if (F_status_is_error(status)) {
@@ -319,7 +316,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status)) {
-      status = controller_thread_main(entry_name, &cache, &thread);
+      status = controller_thread_main(entry_name, data, &setting);
     }
 
     // ensure a newline is always put at the end of the program execution, unless in quiet mode.
@@ -332,12 +329,7 @@ extern "C" {
     f_signal_close(&data->signal);
 
     controller_file_pid_delete(*data, setting.path_pid);
-
-    controller_macro_setting_t_delete_simple(setting)
-    controller_macro_cache_t_delete_simple(cache)
-    controller_macro_mutex_t_delete_simple(mutex)
-    controller_macro_thread_t_delete_simple(thread)
-
+    controller_setting_delete_simple(&setting);
     controller_delete_data(data);
 
     if (status == F_child || status == F_signal) {
@@ -355,13 +347,13 @@ extern "C" {
 #ifndef _di_controller_delete_data_
   f_status_t controller_delete_data(controller_data_t *data) {
 
-    for (f_string_length_t i = 0; i < controller_total_parameters; i++) {
-      f_macro_string_lengths_t_delete_simple(data->parameters[i].locations);
-      f_macro_string_lengths_t_delete_simple(data->parameters[i].locations_sub);
-      f_macro_string_lengths_t_delete_simple(data->parameters[i].values);
+    for (f_array_length_t i = 0; i < controller_total_parameters; i++) {
+      f_macro_array_lengths_t_delete_simple(data->parameters[i].locations);
+      f_macro_array_lengths_t_delete_simple(data->parameters[i].locations_sub);
+      f_macro_array_lengths_t_delete_simple(data->parameters[i].values);
     } // for
 
-    f_macro_string_lengths_t_delete_simple(data->remaining);
+    f_macro_array_lengths_t_delete_simple(data->remaining);
     f_macro_color_context_t_delete_simple(data->context);
 
     return F_none;
index 772122225d0f13ddd176bb94b36f6a5fb517aeb2..789070ba143dc1f16bd15ca50e13c0b5ab3063a5 100644 (file)
@@ -166,7 +166,7 @@ extern "C" {
   typedef struct {
     f_console_parameter_t parameters[controller_total_parameters];
 
-    f_string_lengths_t remaining;
+    f_array_lengths_t remaining;
     bool process_pipe;
 
     f_file_t output;
@@ -184,7 +184,7 @@ extern "C" {
   #define controller_data_t_initialize \
     { \
       controller_console_parameter_t_initialize, \
-      f_string_lengths_t_initialize, \
+      f_array_lengths_t_initialize, \
       F_false, \
       f_macro_file_t_initialize2(f_type_output, f_type_descriptor_output, f_file_flag_write_only), \
       fll_error_print_t_initialize, \
index 7488944509d1abf570681cd3b1dbdd20832375ce..bd3c4d68590a7cd3631d3fd72aafc68020f1366e 100644 (file)
@@ -5,11 +5,21 @@
 extern "C" {
 #endif
 
+#ifndef _di_controller_asynchronous_delete_simple_
+  void controller_asynchronous_delete_simple(controller_asynchronous_t *asynchronous) {
+
+    f_macro_array_lengths_t_delete_simple(asynchronous->stack)
+
+    controller_cache_delete_simple(&asynchronous->cache);
+    controller_rule_delete_simple(&asynchronous->rule);
+  }
+#endif // _di_controller_asynchronous_delete_simple_
+
 #ifndef _di_controller_asynchronouss_increase_
   f_status_t controller_asynchronouss_increase(controller_asynchronouss_t *asynchronouss) {
 
     if (asynchronouss->used + 1 > asynchronouss->size) {
-      f_array_length_t size = asynchronouss->used + controller_thread_asynchronous_allocation_step;
+      f_array_length_t size = asynchronouss->used + controller_default_allocation_step;
 
       if (size > f_array_length_t_size) {
         if (asynchronouss->used + 1 > f_array_length_t_size) {
@@ -32,7 +42,7 @@ extern "C" {
     f_status_t status = F_none;
 
     for (f_array_length_t i = length; i < asynchronouss->size; ++i) {
-      controller_macro_asynchronous_t_delete_simple(asynchronouss->array[i]);
+      controller_asynchronous_delete_simple(&asynchronouss->array[i]);
     } // for
 
     status = f_memory_resize(asynchronouss->size, length, sizeof(controller_asynchronous_t), (void **) & asynchronouss->array);
@@ -49,13 +59,238 @@ extern "C" {
   }
 #endif // _di_controller_asynchronouss_resize_
 
+#ifndef _di_controller_cache_action_delete_simple_
+  void controller_cache_action_delete_simple(controller_cache_action_t *cache) {
+
+    f_string_dynamic_resize(0, &cache->name_action);
+    f_string_dynamic_resize(0, &cache->name_file);
+    f_string_dynamic_resize(0, &cache->name_item);
+    f_string_dynamic_resize(0, &cache->generic);
+    f_string_dynamic_resize(0, &cache->name_action);
+  }
+#endif // _di_controller_cache_action_delete_simple_
+
+#ifndef _di_controller_cache_delete_simple_
+  void controller_cache_delete_simple(controller_cache_t *cache) {
+
+    f_macro_array_lengths_t_delete_simple(cache->ats)
+    f_macro_array_lengths_t_delete_simple(cache->stack)
+    f_macro_fss_delimits_t_delete_simple(cache->delimits)
+
+    f_string_dynamic_resize(0, &cache->buffer_file);
+    f_string_dynamic_resize(0, &cache->buffer_item);
+    f_string_dynamic_resize(0, &cache->buffer_path);
+
+    f_string_ranges_resize(0, &cache->comments);
+    f_string_ranges_resize(0, &cache->content_action);
+    f_string_ranges_resize(0, &cache->object_actions);
+    f_string_ranges_resize(0, &cache->object_items);
+
+    f_string_rangess_resize(0, &cache->content_actions);
+    f_string_rangess_resize(0, &cache->content_items);
+
+    controller_cache_action_delete_simple(&cache->action);
+  }
+#endif // _di_controller_cache_delete_simple_
+
+#ifndef _di_controller_entry_action_delete_simple_
+  void controller_entry_action_delete_simple(controller_entry_action_t *action) {
+
+    f_string_dynamics_resize(0, &action->parameters);
+  }
+#endif // _di_controller_entry_action_delete_simple_
+
+#ifndef _di_controller_entry_actions_delete_simple_
+  void controller_entry_actions_delete_simple(controller_entry_actions_t *actions) {
+
+    actions->used = actions->size;
+
+    while (actions->used) {
+      controller_entry_action_delete_simple(&actions->array[--actions->used]);
+    } // while
+
+    f_memory_delete(actions->size, sizeof(controller_entry_action_t), (void **) & actions->array);
+    actions->size = 0;
+  }
+#endif // _di_controller_entry_actions_delete_simple_
+
+#ifndef _di_controller_entry_item_delete_simple_
+  void controller_entry_item_delete_simple(controller_entry_item_t *item) {
+
+    f_string_dynamic_resize(0, &item->name);
+  }
+#endif // _di_controller_entry_item_delete_simple_
+
+#ifndef _di_controller_entry_items_delete_simple_
+  void controller_entry_items_delete_simple(controller_entry_items_t *items) {
+
+    items->used = items->size;
+
+    while (items->used) {
+      controller_entry_item_delete_simple(&items->array[--items->used]);
+    } // while
+
+    f_memory_delete(items->size, sizeof(controller_entry_item_t), (void **) & items->array);
+    items->size = 0;
+  }
+#endif // _di_controller_entry_items_delete_simple_
+
+#ifndef _di_controller_error_print_locked_
+  void controller_error_print_locked(const fll_error_print_t error, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) {
+
+    if (error.verbosity != f_console_verbosity_quiet) {
+      f_thread_mutex_lock(&thread->mutex.print);
+
+      fll_error_print(error, status, function, fallback);
+
+      f_thread_mutex_unlock(&thread->mutex.print);
+    }
+  }
+#endif // _di_controller_error_print_locked_
+
+#ifndef _di_controller_process_delete_simple_
+  void controller_process_delete_simple(controller_process_t *process) {
+
+    f_string_dynamic_resize(0, &process->id);
+    f_thread_mutex_delete(&process->lock);
+    f_thread_condition_delete(&process->wait);
+  }
+#endif // _di_controller_process_delete_simple_
+
+#ifndef _di_controller_processs_delete_simple_
+  void controller_processs_delete_simple(controller_processs_t *processs) {
+
+    controller_processs_resize(0, processs);
+  }
+#endif // _di_controller_processs_delete_simple_
+
+#ifndef _di_controller_processs_increase_
+  f_status_t controller_processs_increase(controller_processs_t *processs) {
+
+    if (processs->used + 1 > processs->size) {
+      f_array_length_t size = processs->used + controller_default_allocation_step;
+
+      if (size > f_array_length_t_size) {
+        if (processs->used + 1 > f_array_length_t_size) {
+          return F_status_set_error(F_array_too_large);
+        }
+
+        size = f_array_length_t_size;
+      }
+
+      return controller_processs_resize(size, processs);
+    }
+
+    return F_data_not;
+  }
+#endif // _di_controller_processs_increase_
+
+#ifndef _di_controller_processs_resize_
+  f_status_t controller_processs_resize(const f_array_length_t length, controller_processs_t *processs) {
+
+    f_status_t status = F_none;
+
+    for (f_array_length_t i = length; i < processs->size; ++i) {
+      controller_process_delete_simple(&processs->array[i]);
+    } // for
+
+    status = f_memory_resize(processs->size, length, sizeof(controller_process_t), (void **) & processs->array);
+
+    if (F_status_is_error_not(status)) {
+      processs->size = length;
+
+      if (processs->used > processs->size) {
+        processs->used = length;
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_processs_resize_
+
+#ifndef _di_controller_rule_action_delete_simple_
+  void controller_rule_action_delete_simple(controller_rule_action_t *action) {
+
+    f_string_dynamics_resize(0, &action->parameters);
+  }
+#endif // _di_controller_rule_action_delete_simple_
+
+#ifndef _di_controller_rule_actions_delete_simple_
+  void controller_rule_actions_delete_simple(controller_rule_actions_t *actions) {
+
+    actions->used = actions->size;
+
+    while (actions->used) {
+      controller_rule_action_delete_simple(&actions->array[--actions->used]);
+    } // while
+
+    f_memory_delete(actions->size, sizeof(controller_rule_action_t), (void **) & actions->array);
+    actions->size = 0;
+  }
+#endif // _di_controller_rule_actions_delete_simple_
+
+#ifndef _di_controller_rule_delete_simple_
+  void controller_rule_delete_simple(controller_rule_t *rule) {
+
+    f_macro_thread_mutex_t_delete_simple(rule->lock)
+    f_macro_thread_condition_t_delete_simple(rule->wait)
+    f_macro_string_maps_t_delete_simple(rule->define)
+    f_macro_string_maps_t_delete_simple(rule->parameter)
+    f_macro_string_dynamics_t_delete_simple(rule->environment)
+    f_macro_string_dynamics_t_delete_simple(rule->need)
+    f_macro_string_dynamics_t_delete_simple(rule->want)
+    f_macro_string_dynamics_t_delete_simple(rule->wish)
+    f_macro_int32s_t_delete_simple(rule->affinity)
+    f_macro_control_group_t_delete_simple(rule->control_group)
+    f_macro_int32s_t_delete_simple(rule->groups)
+    f_macro_limit_sets_t_delete_simple(rule->limits)
+
+    f_string_dynamic_resize(0, &rule->id);
+    f_string_dynamic_resize(0, &rule->name);
+    f_string_dynamic_resize(0, &rule->path);
+    f_string_dynamic_resize(0, &rule->script);
+
+    f_capability_delete(&rule->capability);
+
+    controller_rule_items_delete_simple(&rule->items);
+  }
+#endif // _di_controller_rule_delete_simple_
+
+#ifndef _di_controller_rule_item_delete_simple_
+  void controller_rule_item_delete_simple(controller_rule_item_t *item) {
+
+    f_string_dynamic_resize(0, item->actions);
+  }
+#endif // _di_controller_rule_item_delete_simple_
+
+#ifndef _di_controller_rule_items_delete_simple_
+  void controller_rule_items_delete_simple(controller_rule_items_t *items) {
+
+    items->used = items->size;
+
+    while (items->used) {
+      controller_rule_item_delete_simple(&items->array[--items->used]);
+    } // while
+
+    f_memory_delete(items->size, sizeof(controller_rule_item_t), (void **) & items->array);
+    items->size = 0;
+  }
+#endif // _di_controller_rule_items_delete_simple_
+
+#ifndef _di_controller_rules_delete_simple_
+  void controller_rules_delete_simple(controller_rules_t *rules) {
+
+    controller_rules_resize(0, rules);
+  }
+#endif // _di_controller_rules_delete_simple_
+
 #ifndef _di_controller_rules_increase_
   f_status_t controller_rules_increase(controller_rules_t *rules) {
 
     if (rules->used + 1 > rules->size) {
       f_array_length_t size = rules->used + controller_default_allocation_step;
 
-      if (size > f_string_length_t_size) {
+      if (size > f_array_length_t_size) {
         if (rules->used + 1 > f_array_length_t_size) {
           return F_status_set_error(F_array_too_large);
         }
@@ -63,31 +298,59 @@ extern "C" {
         size = f_array_length_t_size;
       }
 
-      const f_status_t status = f_memory_resize(rules->size, size, sizeof(controller_rule_t), (void **) & rules->array);
-
-      if (F_status_is_error_not(status)) {
-        rules->size = size;
-      }
-
-      return status;
+      return controller_rules_resize(size, rules);
     }
 
-    return F_none;
+    return F_data_not;
   }
 #endif // _di_controller_rules_increase_
 
-#ifndef _di_controller_error_print_locked_
-  void controller_error_print_locked(const fll_error_print_t error, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) {
+#ifndef _di_controller_rules_resize_
+  f_status_t controller_rules_resize(const f_array_length_t length, controller_rules_t *rules) {
 
-    if (error.verbosity != f_console_verbosity_quiet) {
-      f_thread_mutex_lock(&thread->mutex->print);
+    f_status_t status = F_none;
 
-      fll_error_print(error, status, function, fallback);
+    for (f_array_length_t i = length; i < rules->size; ++i) {
+      controller_rule_delete_simple(&rules->array[i]);
+    } // for
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+    status = f_memory_resize(rules->size, length, sizeof(controller_rule_t), (void **) & rules->array);
+
+    if (F_status_is_error_not(status)) {
+      rules->size = length;
+
+      if (rules->used > rules->size) {
+        rules->used = length;
+      }
     }
+
+    return status;
   }
-#endif // _di_controller_error_print_locked_
+#endif // _di_controller_rules_resize_
+
+#ifndef _di_controller_setting_delete_simple_
+  void controller_setting_delete_simple(controller_setting_t *setting) {
+
+    f_string_dynamic_resize(0, &setting->path_control);
+    f_string_dynamic_resize(0, &setting->path_pid);
+    f_string_dynamic_resize(0, &setting->path_setting);
+
+    controller_entry_items_delete_simple(&setting->entry.items);
+    controller_rules_delete_simple(&setting->rules);
+  }
+#endif // _di_controller_setting_delete_simple_
+
+#ifndef _di_controller_thread_delete_simple_
+  void controller_thread_delete_simple(controller_thread_t *thread) {
+
+    f_thread_mutex_unlock(&thread->mutex.asynchronous);
+    f_thread_mutex_unlock(&thread->mutex.print);
+    f_thread_mutex_unlock(&thread->mutex.process);
+    f_thread_mutex_unlock(&thread->mutex.rule);
+
+    controller_asynchronouss_resize(0, &thread->asynchronouss);
+  }
+#endif // _di_controller_thread_delete_simple_
 
 #ifdef __cplusplus
 } // extern "C"
index 2215a9cf34d076eeec39afb82a8b09ce73dba198..c284b48466aebee10afde7e9fec5853e86039f08 100644 (file)
@@ -295,27 +295,49 @@ extern "C" {
   };
 #endif // _di_controller_resource_limit_t_
 
+#ifndef _di_controller_execute_set_t_
+  typedef struct {
+    fl_execute_parameter_t parameter;
+    fl_execute_as_t as;
+  } controller_execute_set_t;
+
+  #define controller_execute_set_t_initialize { \
+    fl_execute_parameter_t_initialize, \
+    fl_execute_as_t_initialize \
+  }
+
+  #define controller_macro_execute_set_t_initialize(option, environment, signals, data, as) { \
+    fl_macro_execute_parameter_t_initialize(option, environment, signals, data), \
+    as \
+  }
+
+  #define controller_macro_execute_set_t_clear(set) \
+    fl_macro_execute_parameter_t_clear(set.parameter) \
+    fl_macro_execute_as_t_clear(set.as)
+#endif // _di_controller_execute_set_t_
+
+/**
+ * A structure for sharing mutexes globally between different threads.
+ *
+ * The asynchronous lock is intended to lock any activity on the asynchronouss structure.
+ * 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.
+ */
 #ifndef _di_controller_mutex_t_
   typedef struct {
     f_thread_mutex_t asynchronous;
-    f_thread_mutex_t cache;
     f_thread_mutex_t print;
+    f_thread_mutex_t process;
     f_thread_mutex_t rule;
   } controller_mutex_t;
 
-  #define controller_mutex_t_initialize \
-    { \
-      f_thread_mutex_t_initialize, \
-      f_thread_mutex_t_initialize, \
-      f_thread_mutex_t_initialize, \
-      f_thread_mutex_t_initialize, \
-    }
-
-  #define controller_macro_mutex_t_delete_simple(mutex) \
-    f_thread_mutex_delete(&mutex.asynchronous); \
-    f_thread_mutex_delete(&mutex.cache); \
-    f_thread_mutex_delete(&mutex.print); \
-    f_thread_mutex_delete(&mutex.rule);
+  #define controller_mutex_t_initialize { \
+    f_thread_mutex_t_initialize, \
+    f_thread_mutex_t_initialize, \
+    f_thread_mutex_t_initialize, \
+    f_thread_mutex_t_initialize \
+  }
 #endif // _di_controller_mutex_t_
 
 #ifndef _di_controller_rule_action_t_
@@ -348,24 +370,24 @@ extern "C" {
 
   typedef struct {
     uint8_t type;
-
-    f_string_length_t line;
-
+    f_array_length_t line;
     f_status_t status;
 
     f_string_dynamics_t parameters;
   } controller_rule_action_t;
 
-  #define controller_rule_action_t_initialize \
-    { \
-      0, \
-      0, \
-      F_known_not, \
-      f_string_dynamics_t_initialize, \
-    }
-
-  #define controller_macro_rule_action_t_delete_simple(action) \
-    f_macro_string_dynamics_t_delete_simple(action.parameters);
+  #define controller_rule_action_t_initialize { \
+    0, \
+    0, \
+    F_known_not, \
+    f_string_dynamics_t_initialize, \
+  }
+
+  #define controller_macro_rule_action_t_clear(rule) \
+    type = 0; \
+    line = 0; \
+    status = F_known_not; \
+    f_macro_string_dynamics_t_clear(rule.parameters)
 #endif // _di_controller_rule_action_t_
 
 #ifndef _di_controller_rule_actions_t_
@@ -376,21 +398,16 @@ extern "C" {
     f_array_length_t used;
   } controller_rule_actions_t;
 
-  #define controller_rule_actions_t_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_rule_actions_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_rule_actions_t_delete_simple(actions) \
-    actions.used = actions.size; \
-    while (actions.used) { \
-      actions.used--; \
-      controller_macro_rule_action_t_delete_simple(actions.array[actions.used]); \
-    } \
-    f_memory_delete(actions.size, sizeof(controller_rule_action_t), (void **) & actions.array); \
-    actions.size = 0;
+  #define controller_macro_rule_actions_t_clear(actions) \
+    actions.array = 0; \
+    actions.size = 0; \
+    actions.used = 0;
 #endif // _di_controller_rule_actions_t_
 
 #ifndef _di_controller_rule_item_t_
@@ -403,7 +420,7 @@ extern "C" {
 
   typedef struct {
     uint8_t type;
-    f_string_length_t line;
+    f_array_length_t line;
 
     controller_rule_actions_t actions;
   } controller_rule_item_t;
@@ -415,8 +432,10 @@ extern "C" {
       controller_rule_actions_t_initialize, \
     }
 
-  #define controller_macro_rule_item_t_delete_simple(item) \
-    controller_macro_rule_actions_t_delete_simple(item.actions)
+  #define controller_macro_rule_item_t_clear(item) \
+    item.type = 0; \
+    item.line = 0; \
+    controller_macro_rule_actions_t_clear(item.actions);
 #endif // _di_controller_rule_item_t_
 
 #ifndef _di_controller_rule_items_t_
@@ -427,21 +446,16 @@ extern "C" {
     f_array_length_t used;
   } controller_rule_items_t;
 
-  #define controller_rule_items_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_rule_items_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_rule_items_t_delete_simple(items) \
-    items.used = items.size; \
-    while (items.used) { \
-      items.used--; \
-      controller_macro_rule_item_t_delete_simple(items.array[items.used]); \
-    } \
-    f_memory_delete(items.size, sizeof(controller_rule_item_t), (void **) & items.array); \
-    items.size = 0;
+  #define controller_macro_rule_items_t_clear(items) \
+    items.array = 0; \
+    items.size = 0; \
+    items.used = 0;
 #endif // _di_controller_rule_items_t_
 
 #ifndef _di_controller_rule_t_
@@ -479,11 +493,6 @@ extern "C" {
   #define controller_rule_has_user          0x10
 
   typedef struct {
-    f_status_t status;
-
-    f_thread_mutex_t lock;
-    f_thread_condition_t wait;
-
     f_number_unsigned_t timeout_kill;
     f_number_unsigned_t timeout_start;
     f_number_unsigned_t timeout_stop;
@@ -518,56 +527,60 @@ extern "C" {
     controller_rule_items_t items;
   } controller_rule_t;
 
-  #define controller_rule_t_initialize \
-    { \
-      F_known_not, \
-      f_thread_mutex_t_initialize, \
-      f_thread_condition_t_initialize, \
-      0, \
-      0, \
-      0, \
-      0, \
-      0, \
-      0, \
-      f_time_spec_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_maps_t_initialize, \
-      f_string_maps_t_initialize, \
-      f_string_dynamics_t_initialize, \
-      f_string_dynamics_t_initialize, \
-      f_string_dynamics_t_initialize, \
-      f_string_dynamics_t_initialize, \
-      f_int32s_t_initialize, \
-      f_capability_t_initialize, \
-      f_control_group_t_initialize, \
-      f_int32s_t_initialize, \
-      f_limit_sets_t_initialize, \
-      f_execute_scheduler_t_initialize, \
-      controller_rule_items_initialize, \
-    }
-
-  #define controller_macro_rule_t_delete_simple(rule) \
-    f_macro_thread_mutex_t_delete_simple(rule.lock) \
-    f_macro_thread_condition_t_delete_simple(rule.wait) \
-    f_macro_string_dynamic_t_delete_simple(rule.id) \
-    f_macro_string_dynamic_t_delete_simple(rule.name) \
-    f_macro_string_dynamic_t_delete_simple(rule.path) \
-    f_macro_string_dynamic_t_delete_simple(rule.script) \
-    f_macro_string_maps_t_delete_simple(rule.define) \
-    f_macro_string_maps_t_delete_simple(rule.parameter) \
-    f_macro_string_dynamics_t_delete_simple(rule.environment) \
-    f_macro_string_dynamics_t_delete_simple(rule.need) \
-    f_macro_string_dynamics_t_delete_simple(rule.want) \
-    f_macro_string_dynamics_t_delete_simple(rule.wish) \
-    f_macro_int32s_t_delete_simple(rule.affinity) \
-    f_capability_delete(&rule.capability); \
-    f_macro_control_group_t_delete_simple(rule.control_group) \
-    f_macro_int32s_t_delete_simple(rule.groups) \
-    f_macro_limit_sets_t_delete_simple(rule.limits) \
-    controller_macro_rule_items_t_delete_simple(rule.items)
+  #define controller_rule_t_initialize { \
+    0, \
+    0, \
+    0, \
+    0, \
+    0, \
+    0, \
+    0, \
+    f_time_spec_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_maps_t_initialize, \
+    f_string_maps_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    f_int32s_t_initialize, \
+    f_capability_t_initialize, \
+    f_control_group_t_initialize, \
+    f_int32s_t_initialize, \
+    f_limit_sets_t_initialize, \
+    f_execute_scheduler_t_initialize, \
+    controller_rule_items_initialize, \
+  }
+
+  #define controller_macro_rule_t_clear(rule) \
+    rule.timeout_kill = 0; \
+    rule.timeout_start = 0; \
+    rule.timeout_stop = 0; \
+    rule.has = 0; \
+    rule.nice = 0; \
+    rule.user = 0; \
+    rule.group = 0; \
+    f_macro_time_spec_t_clear(rule.timestamp) \
+    f_macro_string_dynamic_t_clear(rule.id) \
+    f_macro_string_dynamic_t_clear(rule.name) \
+    f_macro_string_dynamic_t_clear(rule.path) \
+    f_macro_string_dynamic_t_clear(rule.script) \
+    f_macro_string_maps_t_clear(rule.define) \
+    f_macro_string_maps_t_clear(rule.parameter) \
+    f_macro_string_dynamics_t_clear(rule.environment) \
+    f_macro_string_dynamics_t_clear(rule.need) \
+    f_macro_string_dynamics_t_clear(rule.want) \
+    f_macro_string_dynamics_t_clear(rule.wish) \
+    f_macro_int32s_t_clear(rule.affinity) \
+    f_macro_capability_t_clear(rule.capability) \
+    f_macro_control_group_t_clear(rule.control_group) \
+    f_macro_int32s_t_clear(rule.groups) \
+    f_macro_limit_sets_t_clear(rule.limits) \
+    f_macro_execute_scheduler_t_clear(rule.scheduler) \
+    controller_macro_rule_items_t_clear(rule.items)
 #endif // _di_controller_rule_t_
 
 #ifndef _di_controller_rules_t_
@@ -578,23 +591,67 @@ extern "C" {
     f_array_length_t used;
   } controller_rules_t;
 
-  #define controller_rules_t_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_rules_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_rules_t_delete_simple(rules) \
-    rules.used = rules.size; \
-    while (rules.used) { \
-      rules.used--; \
-      controller_macro_rule_t_delete_simple(rules.array[rules.used]); \
-    } \
-    f_memory_delete(rules.size, sizeof(controller_rule_t), (void **) & rules.array); \
-    rules.size = 0;
+  #define controller_macro_rules_t_clear(rules) \
+    rules.array = 0; \
+    rules.size = 0; \
+    rules.used = 0;
 #endif // _di_controller_rules_t_
 
+/**
+ * Store controller process information for some rule.
+ *
+ * This refers to "process" as in the processing of a rule 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").
+ */
+#ifndef _di_controller_process_t_
+  typedef struct {
+    f_status_t status;
+    f_string_dynamic_t id;
+
+    f_thread_mutex_t lock;
+    f_thread_condition_t wait;
+  } controller_process_t;
+
+  #define controller_process_t_initialize { \
+    F_known_not, \
+    f_string_dynamic_t_initialize \
+    f_thread_mutex_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_
+
+#ifndef _di_controller_processs_t_
+  typedef struct {
+    controller_process_t *array;
+
+    f_array_length_t size;
+    f_array_length_t used;
+  } controller_processs_t;
+
+  #define controller_processs_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
+
+  #define controller_macro_processs_t_clear(process) \
+    process.array = 0; \
+    process.size = 0; \
+    process.used = 0;
+#endif // _di_controller_processs_t_
+
 #ifndef _di_controller_entry_action_t_
   enum {
     controller_entry_action_type_consider = 1,
@@ -617,7 +674,7 @@ extern "C" {
     uint8_t type;
     uint8_t code;
 
-    f_string_length_t line;
+    f_array_length_t line;
     f_number_unsigned_t number;
 
     f_status_t status;
@@ -625,18 +682,22 @@ extern "C" {
     f_string_dynamics_t parameters;
   } controller_entry_action_t;
 
-  #define controller_entry_action_t_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-      0, \
-      F_known_not, \
-      f_string_dynamics_t_initialize, \
-    }
-
-  #define controller_macro_entry_action_t_delete_simple(action) \
-    f_macro_string_dynamics_t_delete_simple(action.parameters);
+  #define controller_entry_action_t_initialize { \
+    0, \
+    0, \
+    0, \
+    0, \
+    F_known_not, \
+    f_string_dynamics_t_initialize, \
+  }
+
+  #define controller_macro_entry_action_t_clear(action) \
+    action.type = 0; \
+    action.code = 0; \
+    action.line = 0; \
+    action.number = 0; \
+    action.status = F_known_not; \
+    f_macro_string_dynamics_t_clear(action.parameters)
 #endif // _di_controller_entry_action_t_
 
 #ifndef _di_controller_entry_actions_t_
@@ -647,28 +708,23 @@ extern "C" {
     f_array_length_t used;
   } controller_entry_actions_t;
 
-  #define controller_entry_actions_t_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_entry_actions_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_entry_actions_t_delete_simple(actions) \
-    actions.used = actions.size; \
-    while (actions.used) { \
-      actions.used--; \
-      controller_macro_entry_action_t_delete_simple(actions.array[actions.used]); \
-    } \
-    f_memory_resize(actions.size, 0, sizeof(controller_entry_action_t), (void **) & actions.array); \
-    actions.size = 0;
+  #define controller_macro_entry_actions_t_clear(actions) \
+    actions.array = 0; \
+    actions.size = 0; \
+    actions.used = 0;
 #endif // _di_controller_entry_actions_t_
 
 #ifndef _di_controller_entry_item_t_
   typedef struct {
-    f_string_length_t line;
-
+    f_array_length_t line;
     f_string_dynamic_t name;
+
     controller_entry_actions_t actions;
   } controller_entry_item_t;
 
@@ -679,9 +735,10 @@ extern "C" {
       controller_entry_actions_t_initialize, \
     }
 
-  #define controller_macro_entry_item_t_delete_simple(item) \
-    f_macro_string_dynamic_t_delete_simple(item.name); \
-    controller_macro_entry_actions_t_delete_simple(item.actions)
+  #define controller_macro_entry_item_t_clear(item) \
+    item.line = 0; \
+    f_macro_string_dynamic_t_clear(item.name) \
+    controller_macro_entry_actions_t_clear(item.actions)
 #endif // _di_controller_entry_item_t_
 
 #ifndef _di_controller_entry_items_t_
@@ -692,37 +749,33 @@ extern "C" {
     f_array_length_t used;
   } controller_entry_items_t;
 
-  #define controller_entry_items_t_initialize \
-    { \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_entry_items_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_entry_items_t_delete_simple(items) \
-    items.used = items.size; \
-    while (items.used) { \
-      items.used--; \
-      controller_macro_entry_item_t_delete_simple(items.array[items.used]); \
-    } \
-    f_memory_delete(items.size, sizeof(controller_entry_item_t), (void **) & items.array); \
-    items.size = 0;
+  #define controller_macro_entry_items_t_clear(items) \
+    items.array = 0; \
+    items.size = 0; \
+    items.used = 0;
 #endif // _di_controller_entry_items_t_
 
 #ifndef _di_controller_entry_t_
   typedef struct {
     f_status_t status;
+
     controller_entry_items_t items;
   } controller_entry_t;
 
-  #define controller_entry_t_initialize \
-    { \
-      F_known_not, \
-      controller_entry_items_t_initialize, \
-    }
+  #define controller_entry_t_initialize { \
+    F_known_not, \
+    controller_entry_items_t_initialize, \
+  }
 
-  #define controller_macro_entry_t_delete_simple(entry) \
-    controller_macro_entry_items_t_delete_simple(entry.items)
+  #define controller_macro_entry_t_clear(entry) \
+    entry.status = F_known_not; \
+    controller_macro_entry_items_t_clear(entry.items)
 #endif // _di_controller_entry_t_
 
 #ifndef _di_controller_setting_t
@@ -755,35 +808,42 @@ extern "C" {
     controller_rules_t rules;
   } controller_setting_t;
 
-  #define controller_setting_t_initialize \
-    { \
-      F_false, \
-      0, \
-      0, \
-      3, \
-      3, \
-      3, \
-      F_false, \
-      0, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      controller_entry_t_initialize, \
-      controller_rules_t_initialize, \
-    }
-
-  #define controller_macro_setting_t_delete_simple(setting) \
-    f_macro_string_dynamic_t_delete_simple(setting.path_control) \
-    f_macro_string_dynamic_t_delete_simple(setting.path_pid) \
-    f_macro_string_dynamic_t_delete_simple(setting.path_setting) \
-    controller_macro_entry_t_delete_simple(setting.entry) \
-    controller_macro_rules_t_delete_simple(setting.rules)
+  #define controller_setting_t_initialize { \
+    F_false, \
+    0, \
+    0, \
+    3, \
+    3, \
+    3, \
+    F_false, \
+    0, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    controller_entry_t_initialize, \
+    controller_rules_t_initialize, \
+  }
+
+  #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; \
+    setting.failsafe_enabled = F_false; \
+    setting.failsafe_rule_id = 0; \
+    f_macro_string_dynamic_t_clear(entry.path_control) \
+    f_macro_string_dynamic_t_clear(entry.path_pid) \
+    f_macro_string_dynamic_t_clear(entry.path_setting) \
+    controller_macro_entry_t_clear(entry.entry) \
+    controller_macro_rules_t_clear(entry.setting)
 #endif // _di_controller_setting_t
 
 #ifndef _di_controller_cache_action_t_
   typedef struct {
-    f_string_length_t line_action;
-    f_string_length_t line_item;
+    f_array_length_t line_action;
+    f_array_length_t line_item;
 
     f_string_dynamic_t name_action;
     f_string_dynamic_t name_file;
@@ -792,29 +852,22 @@ extern "C" {
     f_string_dynamic_t generic;
   } controller_cache_action_t;
 
-  #define controller_cache_action_t_initialize \
-    { \
-      0, \
-      0, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-    }
+  #define controller_cache_action_t_initialize { \
+    0, \
+    0, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+  }
 
   #define controller_macro_cache_action_t_clear(cache) \
     cache.line_action = 0; \
     cache.line_item = 0; \
-    cache.name_action.used = 0; \
-    cache.name_file.used = 0; \
-    cache.name_item.used = 0; \
-    cache.generic.used = 0;
-
-  #define controller_macro_cache_action_t_delete_simple(cache) \
-    f_macro_string_dynamic_t_delete_simple(cache.name_action) \
-    f_macro_string_dynamic_t_delete_simple(cache.name_file) \
-    f_macro_string_dynamic_t_delete_simple(cache.name_item) \
-    f_macro_string_dynamic_t_delete_simple(cache.generic)
+    f_macro_string_dynamic_t_clear(cache.name_action) \
+    f_macro_string_dynamic_t_clear(cache.name_file) \
+    f_macro_string_dynamic_t_clear(cache.name_item) \
+    f_macro_string_dynamic_t_clear(cache.generic)
 #endif // _di_controller_cache_action_t_
 
 #ifndef _di_controller_cache_t_
@@ -842,39 +895,41 @@ extern "C" {
     controller_cache_action_t action;
   } controller_cache_t;
 
-  #define controller_cache_t_initialize \
-    { \
-      f_time_spec_t_initialize, \
-      f_string_range_t_initialize, \
-      f_array_lengths_t_initialize, \
-      f_array_lengths_t_initialize, \
-      f_fss_comments_t_initialize, \
-      f_fss_delimits_t_initialize, \
-      f_fss_content_t_initialize, \
-      f_fss_contents_t_initialize, \
-      f_fss_contents_t_initialize, \
-      f_fss_objects_t_initialize, \
-      f_fss_objects_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      f_string_dynamic_t_initialize, \
-      controller_cache_action_t_initialize, \
-    }
-
-  #define controller_macro_cache_t_delete_simple(cache) \
-    f_macro_array_lengths_t_delete_simple(cache.ats) \
-    f_macro_array_lengths_t_delete_simple(cache.stack) \
-    f_macro_fss_comments_t_delete_simple(cache.comments) \
-    f_macro_fss_delimits_t_delete_simple(cache.delimits) \
-    f_macro_fss_content_t_delete_simple(cache.content_action) \
-    f_macro_fss_contents_t_delete_simple(cache.content_actions) \
-    f_macro_fss_contents_t_delete_simple(cache.content_items) \
-    f_macro_fss_objects_t_delete_simple(cache.object_actions) \
-    f_macro_fss_objects_t_delete_simple(cache.object_items) \
-    f_macro_string_dynamic_t_delete_simple(cache.buffer_file) \
-    f_macro_string_dynamic_t_delete_simple(cache.buffer_item) \
-    f_macro_string_dynamic_t_delete_simple(cache.buffer_path) \
-    controller_macro_cache_action_t_delete_simple(cache.action)
+  #define controller_cache_t_initialize { \
+    f_time_spec_t_initialize, \
+    f_string_range_t_initialize, \
+    f_array_lengths_t_initialize, \
+    f_array_lengths_t_initialize, \
+    f_fss_comments_t_initialize, \
+    f_fss_delimits_t_initialize, \
+    f_fss_content_t_initialize, \
+    f_fss_contents_t_initialize, \
+    f_fss_contents_t_initialize, \
+    f_fss_objects_t_initialize, \
+    f_fss_objects_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    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_
 
 #ifndef _di_controller_asynchronous_t_
@@ -894,12 +949,23 @@ extern "C" {
     uint8_t options;
     pid_t child;
 
-    void *thread;
     f_array_lengths_t stack;
-    controller_cache_action_t cache;
+    controller_cache_t cache;
+    controller_rule_t rule;
   } controller_asynchronous_t;
 
-  #define controller_asynchronous_t_initialize { f_thread_id_t_initialize, f_thread_mutex_t_initialize, 0, 0, 0, 0, 0, 0, f_array_lengths_t_initialize, controller_cache_action_t_initialize }
+  #define controller_asynchronous_t_initialize { \
+    f_thread_id_t_initialize, \
+    f_thread_mutex_t_initialize, \
+    0, \
+    0, \
+    0, \
+    0, \
+    0, \
+    f_array_lengths_t_initialize, \
+    controller_cache_t_initialize, \
+    controller_rule_t_initialize \
+  }
 
   #define controller_macro_asynchronous_t_clear(asynchronous) \
     f_macro_thread_id_t_clear(asynchronous.id) \
@@ -908,114 +974,127 @@ extern "C" {
     asynchronous.action = 0; \
     asynchronous.options = 0; \
     asynchronous.child = 0; \
-    asynchronous.thread = 0; \
     f_macro_array_lengths_t_clear(asynchronous.stack) \
-    controller_macro_cache_action_t_clear(asynchronous.cache)
-
-  #define controller_macro_asynchronous_t_delete_simple(asynchronous) \
-    controller_macro_cache_action_t_delete_simple(asynchronous.cache) \
-    f_macro_array_lengths_t_delete_simple(asynchronous.stack)
+    controller_macro_cache_t_clear(asynchronous.cache) \
+    controller_macro_rule_t_clear(asynchronous.rule)
 #endif // _di_controller_asynchronous_t_
 
 #ifndef _di_controller_asynchronouss_t_
   typedef struct {
-    bool enabled;
-
     controller_asynchronous_t *array;
 
     f_array_length_t size;
     f_array_length_t used;
   } controller_asynchronouss_t;
 
-  #define controller_asynchronouss_t_initialize \
-    { \
-      F_true, \
-      0, \
-      0, \
-      0, \
-    }
+  #define controller_asynchronouss_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
 
-  #define controller_macro_asynchronouss_t_delete_simple(asynchronouss) \
-    asynchronouss.used = asynchronouss.size; \
-    while (asynchronouss.used) { \
-      asynchronouss.used--; \
-      controller_macro_asynchronous_t_delete_simple(asynchronouss.array[asynchronouss.used]) \
-    } \
-    f_memory_delete(asynchronouss.size, sizeof(controller_asynchronous_t), (void **) & asynchronouss.array); \
-    asynchronouss.size = 0;
+  #define controller_macro_asynchronouss_t_clear(asynchronouss) \
+    asynchronouss.array = 0; \
+    asynchronouss.size = 0; \
+    asynchronouss.used = 0;
 #endif // _di_controller_asynchronouss_t_
 
 #ifndef _di_controller_thread_t_
-  #define controller_thread_cache_cleanup_interval_long  3600  // 1 hour in seconds.
-  #define controller_thread_cache_cleanup_interval_short 180   // 3 minutes in seconds.
-  #define controller_thread_asynchronous_allocation_step 16    // Total number of asynchronous threads increase by.
-  #define controller_thread_asynchronous_total           65535 // Total number of asynchronous threads allowed at any one time.
-
   typedef struct {
-    controller_cache_t *cache_main;
-    controller_cache_action_t *cache_action;
-    controller_data_t *data;
-    controller_mutex_t *mutex;
-    controller_setting_t *setting;
-    f_array_lengths_t *stack;
+    bool enabled;
+
+    f_thread_id_t id_cleanup;
+    f_thread_id_t id_control;
+    f_thread_id_t id_rule;
+    f_thread_id_t id_signal;
+
+    controller_mutex_t mutex;
     controller_asynchronouss_t asynchronouss;
   } controller_thread_t;
 
-  #define controller_thread_t_initialize { 0, 0, 0, 0, 0, 0, controller_asynchronouss_t_initialize }
-
-  #define controller_macro_thread_t_initialize(cache_main, cache_action, data, mutex, setting, stack) { cache_main, cache_action, data, mutex, setting, stack, controller_asynchronouss_t_initialize }
+  #define controller_thread_t_initialize { \
+    F_true, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
+    controller_mutex_t_initialize, \
+    controller_asynchronouss_t_initialize \
+  }
+
+  #define controller_macro_thread_t_initialize(mutex, asynchronouss) { \
+    F_true, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
+    f_thread_id_t_initialize. \
+    mutex, \
+    asynchronouss \
+  }
 
   #define controller_macro_thread_t_clear(thread) \
-    thread.cache_main = 0; \
-    thread.cache_action = 0; \
-    thread.data = 0; \
-    thread.mutex = 0; \
-    thread.setting = 0; \
-    thread.stack = 0; \
-    thread.asynchronouss.used = 0;
-
-  #define controller_macro_thread_t_delete_simple(thread) \
-    controller_asynchronouss_resize(0, &thread.asynchronouss);
-#endif // _di_controller_thread_t_
-
-#ifndef _di_controller_execute_set_t_
-  typedef struct {
-    fl_execute_parameter_t parameter;
-    fl_execute_as_t as;
-  } controller_execute_set_t;
+    thread.enabled = F_true; \
+    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); \
+    f_macro_thread_id_t_clear(thread.id_signal); \
+    controller_macro_mutex_t_clear(thread.mutex), \
+    controller_macro_asynchronouss_t_clear(thread.asynchronouss)
+#endif // _di_controller_data_common_t_
+
+#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.
+  #define controller_thread_cache_cleanup_interval_short 180   // 3 minutes in seconds.
 
-  #define controller_execute_set_t_initialize { fl_execute_parameter_t_initialize, fl_execute_as_t_initialize }
+  // @fixme these aren't used? consider removing or updating.
+  #define controller_thread_asynchronous_allocation_step 16    // Total number of asynchronous threads increase by.
+  #define controller_thread_asynchronous_max             65535 // Total number of asynchronous threads allowed at any one time.
 
-  #define controller_macro_execute_set_t_initialize(option, environment, signals, data, as) { fl_macro_execute_parameter_t_initialize(option, environment, signals, data), as }
+  typedef struct {
+    f_array_length_t id;
 
-  #define controller_macro_execute_set_t_clear(set) \
-    fl_macro_execute_parameter_t_clear(set.parameter) \
-    fl_macro_execute_as_t_clear(set.as)
-#endif // _di_controller_execute_set_t_
+    controller_data_t *data;
+    controller_setting_t *setting;
+    controller_processs_t *processs;
+    controller_thread_t *thread;
+  } controller_thread_data_t;
+
+  #define controller_thread_data_t_initialize { 0, 0, 0, 0, 0 }
+
+  #define controller_macro_thread_data_t_initialize(id, data, setting, processs, thread) { \
+    id, \
+    data, \
+    setting, \
+    processs, \
+    thread \
+  }
+
+  #define controller_macro_thread_data_t_clear(thread_data) \
+    thread_data.id = 0; \
+    thread_data.data = 0; \
+    thread_data.setting = 0; \
+    thread_data.processs = 0; \
+    thread_data.thread = 0;
+#endif // _di_controller_thread_data_t_
 
 /**
- * Resize the asynchronouss array to a larger size.
- *
- * This will resize making the string larger based on the given length.
- * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
- * If already set to the maximum buffer size, then the resize will fail.
+ * Fully deallocate all memory for the given asynchronous without caring about return status.
  *
- * @param amount
- *   A positive number representing how much to increase the size by.
- * @param asynchronouss
- *   The asynchronous array to resize.
+ * @param asynchronous
+ *   The asynchronous to deallocate.
  *
- * @return
- *   F_none on success.
- *   F_data_not on success, but there is no reason to increase size (used + amount <= size).
+ * @see f_macro_array_lengths_t_delete_simple()
  *
- *   F_memory_not (with error bit) on out of memory.
- *   F_parameter (with error bit) if a parameter is invalid.
- *   F_array_too_large (with error bit) if the new array length is too large.
+ * @see controller_cache_delete_simple()
+ * @see controller_rule_delete_simple()
  */
+#ifndef _di_controller_asynchronous_delete_simple_
+  extern void controller_asynchronous_delete_simple(controller_asynchronous_t *asynchronous) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_asynchronous_delete_simple_
 
 /**
- * Increase the size of the asynchronouss array, but only if necessary.
+ * Increase the size of the asynchronous array, but only if necessary.
  *
  * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
  * If already set to the maximum buffer size, then the resize will fail.
@@ -1025,18 +1104,20 @@ extern "C" {
  *
  * @return
  *   F_none on success.
- *   F_data_not on success, but there is no reason to increase size (used + controller_thread_asynchronous_allocation_step <= size).
+ *   F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= size).
  *
  *   F_array_too_large (with error bit) if the new array length is too large.
  *   F_memory_not (with error bit) on out of memory.
  *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see controller_asynchronouss_resize()
  */
 #ifndef _di_controller_asynchronouss_increase_
   extern f_status_t controller_asynchronouss_increase(controller_asynchronouss_t *asynchronouss) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_asynchronouss_increase_
 
 /**
- * Resize the string asynchronouss array.
+ * Resize the string asynchronous array.
  *
  * @param length
  *   The new size to use.
@@ -1048,12 +1129,94 @@ extern "C" {
  *
  *   F_memory_not (with error bit) on out of memory.
  *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see f_memory_resize()
  */
 #ifndef _di_controller_asynchronouss_resize_
   extern f_status_t controller_asynchronouss_resize(const f_array_length_t length, controller_asynchronouss_t *asynchronouss) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_asynchronouss_resize_
 
 /**
+ * Fully deallocate all memory for the given cache without caring about return status.
+ *
+ * @param cache
+ *   The cache to deallocate.
+ *
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_cache_action_delete_simple_
+  extern void controller_cache_action_delete_simple(controller_cache_action_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_cache_action_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given cache without caring about return status.
+ *
+ * @param cache
+ *   The cache to deallocate.
+ *
+ * @see f_macro_array_lengths_t_delete_simple()
+ * @see f_macro_fss_delimits_t_delete_simple()
+ *
+ * @see controller_cache_action_delete_simple()
+ * @see f_string_dynamic_resize()
+ * @see f_string_ranges_resize()
+ * @see f_string_rangess_resize()
+ */
+#ifndef _di_controller_cache_delete_simple_
+  extern void controller_cache_delete_simple(controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_cache_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given entry action without caring about return status.
+ *
+ * @param action
+ *   The action to deallocate.
+ *
+ * @see f_string_dynamics_resize()
+ */
+#ifndef _di_controller_entry_action_delete_simple_
+  extern void controller_entry_action_delete_simple(controller_entry_action_t *action) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_action_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given entry actions without caring about return status.
+ *
+ * @param actions
+ *   The entry_actions to deallocate.
+ *
+ * @see controller_entry_action_delete_simple()
+ * @see f_memory_delete()
+ */
+#ifndef _di_controller_entry_actions_delete_simple_
+  extern void controller_entry_actions_delete_simple(controller_entry_actions_t *actions) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_actions_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given entry item without caring about return status.
+ *
+ * @param item
+ *   The item to deallocate.
+ *
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_entry_item_delete_simple_
+  extern void controller_entry_item_delete_simple(controller_entry_item_t *item) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_item_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given entry items without caring about return status.
+ *
+ * @param items
+ *   The entry_items to deallocate.
+ *
+ * @see controller_entry_item_delete_simple()
+ * @see f_memory_delete()
+ */
+#ifndef _di_controller_entry_items_delete_simple_
+  extern void controller_entry_items_delete_simple(controller_entry_items_t *items) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_items_delete_simple_
+
+/**
  * Print the error, locking the print mutex during the print.
  *
  * @param print
@@ -1076,23 +1239,228 @@ extern "C" {
 #endif // _di_controller_error_print_locked_
 
 /**
- * Increase the size of the rules array, but only if necessary.
+ * Fully deallocate all memory for the given process without caring about return status.
  *
- * @param rules
- *   The rules to resize.
+ * @param process
+ *   The process to deallocate.
+ *
+ * @see f_string_dynamic_resize()
+ * @see f_thread_condition_delete()
+ * @see f_thread_mutex_delete()
+ */
+#ifndef _di_controller_process_delete_simple_
+  extern void controller_process_delete_simple(controller_process_t *process) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_process_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given processs without caring about return status.
+ *
+ * @param processs
+ *   The process array to deallocate.
+ *
+ * @see controller_processs_resize()
+ */
+#ifndef _di_controller_processs_delete_simple_
+  extern void controller_processs_delete_simple(controller_processs_t *processs) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_processs_delete_simple_
+
+/**
+ * Increase the size of the rule array, but only if necessary.
+ *
+ * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
+ * If already set to the maximum buffer size, then the resize will fail.
+ *
+ * @param processs
+ *   The process array to resize.
  *
  * @return
  *   F_none on success.
- *   F_array_too_large (with error bit) if the resulting new size is bigger than the max array length.
+ *   F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= size).
  *
- *   Errors (with error bit) from: f_memory_resize().
+ *   F_array_too_large (with error bit) if the new array length is too large.
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see controller_processs_resize()
+ */
+#ifndef _di_controller_processs_increase_
+  extern f_status_t controller_processs_increase(controller_processs_t *processs) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_increase_
+
+/**
+ * Resize the rule array.
+ *
+ * @param length
+ *   The new size to use.
+ * @param processs
+ *   The process array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
  *
  * @see f_memory_resize()
  */
+#ifndef _di_controller_processs_resize_
+  extern f_status_t controller_processs_resize(const f_array_length_t length, controller_processs_t *processs) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_processs_resize_
+
+/**
+ * Fully deallocate all memory for the given rule action without caring about return status.
+ *
+ * @param action
+ *   The action to deallocate.
+ *
+ * @see f_string_dynamics_resize()
+ */
+#ifndef _di_controller_rule_action_delete_simple_
+  extern void controller_rule_action_delete_simple(controller_rule_action_t *action) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_action_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rule actions without caring about return status.
+ *
+ * @param actions
+ *   The rule_actions to deallocate.
+ *
+ * @see controller_rule_action_delete_simple()
+ * @see f_memory_delete()
+ */
+#ifndef _di_controller_rule_actions_delete_simple_
+  extern void controller_rule_actions_delete_simple(controller_rule_actions_t *actions) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_actions_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rule without caring about return status.
+ *
+ * @param rule
+ *   The rule to deallocate.
+ *
+ * @see f_macro_control_group_t_delete_simple()
+ * @see f_macro_int32s_t_delete_simple()
+ * @see f_macro_limit_sets_t_delete_simple()
+ * @see f_macro_string_dynamics_t_delete_simple()
+ * @see f_macro_string_maps_t_delete_simple()
+ * @see f_macro_thread_condition_t_delete_simple()
+ * @see f_macro_thread_mutex_t_delete_simple()
+ *
+ * @see controller_rule_items_delete_simple()
+ * @see f_capability_delete()
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_rule_delete_simple_
+  extern void controller_rule_delete_simple(controller_rule_t *rule) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rule item without caring about return status.
+ *
+ * @param item
+ *   The item to deallocate.
+ *
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_rule_item_delete_simple_
+  extern void controller_rule_item_delete_simple(controller_rule_item_t *item) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_item_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rule items without caring about return status.
+ *
+ * @param items
+ *   The rule_items to deallocate.
+ *
+ * @see controller_rule_item_delete_simple()
+ * @see f_memory_delete()
+ */
+#ifndef _di_controller_rule_items_delete_simple_
+  extern void controller_rule_items_delete_simple(controller_rule_items_t *items) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_items_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rules without caring about return status.
+ *
+ * @param rules
+ *   The rules to deallocate.
+ *
+ * @see controller_rules_resize()
+ */
+#ifndef _di_controller_rules_delete_simple_
+  extern void controller_rules_delete_simple(controller_rules_t *rules) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rules_delete_simple_
+
+/**
+ * Increase the size of the rule array, but only if necessary.
+ *
+ * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
+ * If already set to the maximum buffer size, then the resize will fail.
+ *
+ * @param rules
+ *   The rule array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *   F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= size).
+ *
+ *   F_array_too_large (with error bit) if the new array length is too large.
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see controller_rules_resize()
+ */
 #ifndef _di_controller_rules_increase_
   extern f_status_t controller_rules_increase(controller_rules_t *rules) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_increase_
 
+/**
+ * Resize the rule array.
+ *
+ * @param length
+ *   The new size to use.
+ * @param rules
+ *   The rule array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see f_memory_resize()
+ */
+#ifndef _di_controller_rules_resize_
+  extern f_status_t controller_rules_resize(const f_array_length_t length, controller_rules_t *rules) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rules_resize_
+
+/**
+ * Fully deallocate all memory for the given setting without caring about return status.
+ *
+ * @param setting
+ *   The setting to deallocate.
+ *
+ * @see controller_entry_delete_simple()
+ * @see controller_rules_delete_simple()
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_setting_delete_simple_
+  extern void controller_setting_delete_simple(controller_setting_t *setting) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_setting_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given setting without caring about return status.
+ *
+ * @param thread
+ *   The thread to deallocate.
+ *
+ * @see controller_asynchronouss_resize()
+ * @see f_thread_mutex_unlock()
+ */
+#ifndef _di_controller_thread_delete_simple_
+  extern void controller_thread_delete_simple(controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_thread_delete_simple_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index a976a0a8bef8d9ba627cf908e4925278c1de2085..3b096b4d0d356efeb58f78e24dac74afc37fa85e 100644 (file)
@@ -41,7 +41,7 @@ extern "C" {
 #endif // _di_controller_string_dynamic_partial_append_terminated_
 
 #ifndef _di_controller_file_load_
-  f_status_t controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, controller_cache_t *cache) {
+  f_status_t controller_file_load(const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_array_length_t path_prefix_length, const f_array_length_t path_suffix_length, controller_thread_data_t thread_data, controller_cache_t *cache) {
     f_status_t status = F_none;
     f_file_t file = f_file_t_initialize;
 
@@ -69,25 +69,25 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "f_string_append", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_append", F_true);
       return status;
     }
 
     status = f_string_dynamic_terminate_after(&cache->action.name_file);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
       return status;
     }
 
-    const f_string_length_t path_length = setting.path_setting.used ? setting.path_setting.used + f_path_separator_length + cache->action.name_file.used : cache->action.name_file.used;
+    const f_array_length_t path_length = thread_data.setting->path_setting.used ? thread_data.setting->path_setting.used + f_path_separator_length + cache->action.name_file.used : cache->action.name_file.used;
     char path[path_length + 1];
 
-    if (setting.path_setting.used) {
-      memcpy(path, setting.path_setting.string, setting.path_setting.used);
-      memcpy(path + setting.path_setting.used + f_path_separator_length, cache->action.name_file.string, cache->action.name_file.used);
+    if (thread_data.setting->path_setting.used) {
+      memcpy(path, thread_data.setting->path_setting.string, thread_data.setting->path_setting.used);
+      memcpy(path + thread_data.setting->path_setting.used + f_path_separator_length, cache->action.name_file.string, cache->action.name_file.used);
 
-      path[setting.path_setting.used] = f_path_separator_s[0];
+      path[thread_data.setting->path_setting.used] = f_path_separator_s[0];
     }
 
     path[path_length] = 0;
@@ -95,13 +95,13 @@ extern "C" {
     status = f_file_stream_open(path, 0, &file);
 
     if (F_status_is_error(status)) {
-      fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_open", F_true, path, "open", fll_error_file_type_file);
+      fll_error_file_print(thread_data.data->error, F_status_set_fine(status), "f_file_stream_open", F_true, path, "open", fll_error_file_type_file);
     }
     else {
       status = f_file_stream_read(file, 1, &cache->buffer_file);
 
       if (F_status_is_error(status)) {
-        fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_read", F_true, path, "read", fll_error_file_type_file);
+        fll_error_file_print(thread_data.data->error, F_status_set_fine(status), "f_file_stream_read", F_true, path, "read", fll_error_file_type_file);
       }
     }
 
@@ -113,7 +113,7 @@ extern "C" {
       status = f_file_stat(path, F_true, &stat_file);
 
       if (F_status_is_error(status)) {
-        fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stat", F_true, path, "stat", fll_error_file_type_file);
+        fll_error_file_print(thread_data.data->error, F_status_set_fine(status), "f_file_stat", F_true, path, "stat", fll_error_file_type_file);
       }
       else {
         cache->timestamp.seconds = stat_file.st_ctim.tv_sec;
@@ -304,29 +304,29 @@ extern "C" {
 #endif // _di_controller_get_id_group_
 
 #ifndef _di_controller_perform_ready_
-  f_status_t controller_perform_ready(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
+  f_status_t controller_perform_ready(controller_thread_data_t thread_data, controller_cache_t *cache) {
     f_status_t status = F_none;
 
     // only create pid file when not in validate mode.
-    if (data.parameters[controller_parameter_validate].result == f_console_result_none) {
+    if (thread_data.data->parameters[controller_parameter_validate].result == f_console_result_none) {
 
-      status = controller_file_pid_create(data, setting->path_pid);
+      status = controller_file_pid_create(*thread_data.data, thread_data.setting->path_pid);
 
       // report pid file error but because this could be an "init" program, consider the pid file as optional and continue on.
       if (F_status_is_error(status)) {
 
         // always return immediately on memory errors.
         if (F_status_set_fine(status) == F_memory_not) {
-          fll_error_file_print(data.error, F_status_set_fine(status), "controller_file_pid_create", F_true, setting->path_pid.string, "create", fll_error_file_type_file);
+          fll_error_file_print(thread_data.data->error, F_status_set_fine(status), "controller_file_pid_create", F_true, thread_data.setting->path_pid.string, "create", fll_error_file_type_file);
 
-          controller_entry_error_print(data.error, cache->action);
+          controller_entry_error_print(thread_data.data->error, cache->action);
 
           return status;
         }
 
-        fll_error_file_print(data.warning, F_status_set_fine(status), "controller_file_pid_create", F_true, setting->path_pid.string, "create", fll_error_file_type_file);
+        fll_error_file_print(thread_data.data->warning, F_status_set_fine(status), "controller_file_pid_create", F_true, thread_data.setting->path_pid.string, "create", fll_error_file_type_file);
 
-        controller_entry_error_print(data.warning, cache->action);
+        controller_entry_error_print(thread_data.data->warning, cache->action);
 
         status = F_none;
       }
@@ -337,7 +337,7 @@ extern "C" {
 #endif // _di_controller_perform_ready_
 
 #ifndef _di_controller_preprocess_entry_
-  f_status_t controller_preprocess_entry(controller_cache_t *cache, controller_thread_t *thread) {
+  f_status_t controller_preprocess_entry(controller_thread_data_t thread_data, controller_cache_t *cache) {
     f_status_t status = F_none;
     f_status_t status2 = F_none;
 
@@ -347,25 +347,23 @@ extern "C" {
     f_array_length_t at_i = 0;
     f_array_length_t at_j = 1;
 
-    controller_data_t *data = thread->data;
-    controller_setting_t *setting = thread->setting;
     controller_entry_actions_t *actions = 0;
 
     uint8_t error_has = F_false;
 
-    setting->ready = controller_setting_ready_no;
+    thread_data.setting->ready = controller_setting_ready_no;
 
     cache->ats.used = 0;
 
-    thread->cache_action->line_action = 0;
-    thread->cache_action->line_item = 0;
-    thread->cache_action->name_action.used = 0;
-    thread->cache_action->name_item.used = 0;
+    cache->action.line_action = 0;
+    cache->action.line_item = 0;
+    cache->action.name_action.used = 0;
+    cache->action.name_item.used = 0;
 
     f_macro_array_lengths_t_increase_by(status, cache->ats, controller_default_allocation_step)
 
     if (F_status_is_error(status)) {
-      fll_error_print(data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
       return status;
     }
 
@@ -374,55 +372,55 @@ extern "C" {
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
 
-    thread->cache_action->line_item = setting->entry.items.array[0].line;
-    thread->cache_action->name_item.used = 0;
+    cache->action.line_item = thread_data.setting->entry.items.array[0].line;
+    cache->action.name_item.used = 0;
 
-    status = controller_string_dynamic_append_terminated(setting->entry.items.array[0].name, &thread->cache_action->name_item);
+    status = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[0].name, &cache->action.name_item);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
-      controller_entry_error_print(data->error, *thread->cache_action);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+      controller_entry_error_print(thread_data.data->error, cache->action);
 
       return status;
     }
 
     for (;;) {
 
-      actions = &setting->entry.items.array[cache->ats.array[at_i]].actions;
+      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]) {
 
-        if (setting->signal) {
+        if (thread_data.setting->signal) {
           return F_signal;
         }
 
-        thread->cache_action->line_action = actions->array[cache->ats.array[at_j]].line;
-        thread->cache_action->name_action.used = 0;
+        cache->action.line_action = actions->array[cache->ats.array[at_j]].line;
+        cache->action.name_action.used = 0;
 
-        status2 = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &thread->cache_action->name_action);
+        status2 = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->action.name_action);
 
         if (F_status_is_error(status2)) {
-          fll_error_print(data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
-          controller_entry_error_print(data->error, *thread->cache_action);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(thread_data.data->error, cache->action);
 
           return status2;
         }
 
         if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) {
 
-          if (setting->ready == controller_setting_ready_wait) {
-            if (data->warning.verbosity == f_console_verbosity_debug) {
-              fprintf(data->warning.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->warning.to.stream, "%s%sMultiple '", data->warning.context.before->string, data->warning.prefix ? data->warning.prefix : f_string_empty_s);
-              fprintf(data->warning.to.stream, "%s%s%s%s", data->warning.context.after->string, data->warning.notable.before->string, controller_string_ready_s, data->warning.notable.after->string);
-              fprintf(data->warning.to.stream, "%s' entry item actions detected; only the first will be used.%s%c", data->warning.context.before->string, data->warning.context.after->string, f_string_eol_s[0]);
+          if (thread_data.setting->ready == controller_setting_ready_wait) {
+            if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              fprintf(thread_data.data->warning.to.stream, "%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->warning.to.stream, "%s%sMultiple '", 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%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, controller_string_ready_s, thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s' entry item actions detected; only the first will be used.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
 
-              controller_entry_error_print(data->warning, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->warning, cache->action);
             }
           }
 
           // the pre-process currently only looks for "ready", so once found, pre-process is complete.
-          setting->ready = controller_setting_ready_wait;
+          thread_data.setting->ready = controller_setting_ready_wait;
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
           error_has = F_false;
@@ -432,22 +430,22 @@ extern "C" {
           }
 
           // walk though each items and check to see if the item actually exists (skipping main).
-          for (i = 1; i < setting->entry.items.used; ++i) {
+          for (i = 1; i < thread_data.setting->entry.items.used; ++i) {
 
-            if (fl_string_dynamic_compare(setting->entry.items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
+            if (fl_string_dynamic_compare(thread_data.setting->entry.items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
 
               // check to see if "i" is already in the stack (to prevent recursion) (skipping main).
               for (j = 2; j < cache->ats.used; j += 2) {
 
                 if (cache->ats.array[j] == i) {
-                  if (data->error.verbosity != f_console_verbosity_quiet) {
-                    fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
-                    fprintf(data->error.to.stream, "%s%sThe entry item named '", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
-                    fprintf(data->error.to.stream, "%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, setting->entry.items.array[i].name.string, data->error.notable.after->string);
-                    fprintf(data->error.to.stream, "%s' cannot be executed because recursion is not allowed.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+                  if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                    fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                    fprintf(thread_data.data->error.to.stream, "%s%sThe entry item named '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, thread_data.setting->entry.items.array[i].name.string, thread_data.data->error.notable.after->string);
+                    fprintf(thread_data.data->error.to.stream, "%s' cannot be executed because recursion is not allowed.%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(data->error, *thread->cache_action);
+                  controller_entry_error_print(thread_data.data->error, cache->action);
 
                   if (F_status_is_error_not(status)) {
                     status = F_status_set_error(F_recurse);
@@ -463,8 +461,8 @@ extern "C" {
               f_macro_array_lengths_t_increase_by(status2, cache->ats, controller_default_allocation_step)
 
               if (F_status_is_error(status2)) {
-                fll_error_print(data->error, F_status_set_fine(status2), "f_macro_array_lengths_t_increase_by", F_true);
-                controller_entry_error_print(data->error, *thread->cache_action);
+                fll_error_print(thread_data.data->error, F_status_set_fine(status2), "f_macro_array_lengths_t_increase_by", F_true);
+                controller_entry_error_print(thread_data.data->error, cache->action);
 
                 return status2;
               }
@@ -480,17 +478,17 @@ extern "C" {
               cache->ats.array[at_j] = 0;
               cache->ats.used += 2;
 
-              thread->cache_action->name_action.used = 0;
-              thread->cache_action->line_action = 0;
+              cache->action.name_action.used = 0;
+              cache->action.line_action = 0;
 
-              thread->cache_action->name_item.used = 0;
-              thread->cache_action->line_item = setting->entry.items.array[i].line;
+              cache->action.name_item.used = 0;
+              cache->action.line_item = thread_data.setting->entry.items.array[i].line;
 
-              status2 = controller_string_dynamic_append_terminated(setting->entry.items.array[i].name, &thread->cache_action->name_item);
+              status2 = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[i].name, &cache->action.name_item);
 
               if (F_status_is_error(status2)) {
-                fll_error_print(data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
-                controller_entry_error_print(data->error, *thread->cache_action);
+                fll_error_print(thread_data.data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
+                controller_entry_error_print(thread_data.data->error, cache->action);
 
                 return status2;
               }
@@ -499,16 +497,16 @@ extern "C" {
             }
           } // for
 
-          if (error_has || i >= setting->entry.items.used) {
-            if (i >= setting->entry.items.used) {
-              if (data->error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data->error.to.stream, "%s%sThe entry item named '", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
-                fprintf(data->error.to.stream, "%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, actions->array[cache->ats.array[at_j]].parameters.array[0].string, data->error.notable.after->string);
-                fprintf(data->error.to.stream, "%s' does not exist.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+          if (error_has || i >= thread_data.setting->entry.items.used) {
+            if (i >= thread_data.setting->entry.items.used) {
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%sThe entry item named '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, actions->array[cache->ats.array[at_j]].parameters.array[0].string, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s' does not exist.%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(data->error, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->error, cache->action);
 
               if (F_status_is_error_not(status)) {
                 status = F_status_set_error(F_valid_not);
@@ -521,8 +519,8 @@ extern "C" {
         }
       } // for
 
-      thread->cache_action->line_action = 0;
-      thread->cache_action->name_action.used = 0;
+      cache->action.line_action = 0;
+      cache->action.name_action.used = 0;
 
       // end of actions found, so drop to previous loop in stack.
       if (cache->ats.array[at_j] == actions->used) {
@@ -536,14 +534,14 @@ extern "C" {
         cache->ats.used -= 2;
         cache->ats.array[at_j]++;
 
-        thread->cache_action->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
-        thread->cache_action->name_item.used = 0;
+        cache->action.line_item = thread_data.setting->entry.items.array[cache->ats.array[at_i]].line;
+        cache->action.name_item.used = 0;
 
-        status2 = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &thread->cache_action->name_item);
+        status2 = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
         if (F_status_is_error(status2)) {
-          fll_error_print(data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
-          controller_entry_error_print(data->error, *thread->cache_action);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(thread_data.data->error, cache->action);
 
           return status2;
         }
@@ -551,8 +549,8 @@ extern "C" {
     } // for
 
     // if ready was never found in the entry, then default to always ready.
-    if (setting->ready == controller_setting_ready_no) {
-      setting->ready = controller_setting_ready_yes;
+    if (thread_data.setting->ready == controller_setting_ready_no) {
+      thread_data.setting->ready = controller_setting_ready_yes;
     }
 
     return status;
@@ -560,7 +558,7 @@ extern "C" {
 #endif // _di_controller_preprocess_entry_
 
 #ifndef _di_controller_process_entry_
-  f_status_t controller_process_entry(controller_cache_t *cache, controller_thread_t *thread) {
+  f_status_t controller_process_entry(controller_thread_data_t thread_data, controller_cache_t *cache) {
     f_status_t status = F_none;
 
     f_array_length_t i = 0;
@@ -572,30 +570,28 @@ extern "C" {
 
     uint8_t rule_options = 0;
 
-    controller_data_t *data = thread->data;
-    controller_setting_t *setting = thread->setting;
     controller_entry_actions_t *actions = 0;
 
-    const bool simulate = data->parameters[controller_parameter_test].result == f_console_result_found;
+    const bool simulate = thread_data.data->parameters[controller_parameter_test].result == f_console_result_found;
 
     cache->ats.used = 0;
     cache->stack.used = 0;
 
-    thread->cache_action->line_action = 0;
-    thread->cache_action->line_item = 0;
-    thread->cache_action->name_action.used = 0;
-    thread->cache_action->name_item.used = 0;
+    cache->action.line_action = 0;
+    cache->action.line_item = 0;
+    cache->action.name_action.used = 0;
+    cache->action.name_item.used = 0;
 
-    if (setting->ready == controller_setting_ready_yes) {
-      status = controller_perform_ready(*data, setting, cache);
+    if (thread_data.setting->ready == controller_setting_ready_yes) {
+      status = controller_perform_ready(thread_data, cache);
       if (F_status_is_error(status)) return status;
     }
 
     f_macro_array_lengths_t_increase_by(status, cache->ats, controller_default_allocation_step)
 
     if (F_status_is_error(status)) {
-      fll_error_print(data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
-      controller_entry_error_print(data->error, *thread->cache_action);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
+      controller_entry_error_print(thread_data.data->error, cache->action);
 
       return status;
     }
@@ -605,44 +601,44 @@ extern "C" {
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
 
-    thread->cache_action->line_item = setting->entry.items.array[0].line;
-    thread->cache_action->name_item.used = 0;
+    cache->action.line_item = thread_data.setting->entry.items.array[0].line;
+    cache->action.name_item.used = 0;
 
-    status = controller_string_dynamic_append_terminated(setting->entry.items.array[0].name, &thread->cache_action->name_item);
+    status = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[0].name, &cache->action.name_item);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
-      controller_entry_error_print(data->error, *thread->cache_action);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+      controller_entry_error_print(thread_data.data->error, cache->action);
 
       return status;
     }
 
     if (simulate) {
-      fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-      fprintf(data->output.stream, "Processing entry item rule '");
-      fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, controller_string_main_s, data->context.set.title.after->string);
-      fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
+      fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+      fprintf(thread_data.data->output.stream, "Processing entry item rule '");
+      fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, controller_string_main_s, thread_data.data->context.set.title.after->string);
+      fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]);
     }
 
     for (;;) {
 
-      actions = &setting->entry.items.array[cache->ats.array[at_i]].actions;
+      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]) {
 
-        if (setting->signal) {
+        if (thread_data.setting->signal) {
           status = F_signal;
           break;
         }
 
-        thread->cache_action->line_action = actions->array[cache->ats.array[at_j]].line;
-        thread->cache_action->name_action.used = 0;
+        cache->action.line_action = actions->array[cache->ats.array[at_j]].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), &thread->cache_action->name_action);
+        status = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->action.name_action);
 
         if (F_status_is_error(status)) {
-          fll_error_print(data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
-          controller_entry_error_print(data->error, *thread->cache_action);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(thread_data.data->error, cache->action);
 
           return status;
         }
@@ -652,91 +648,91 @@ extern "C" {
           if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
 
             if (simulate) {
-              fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->output.stream, "The entry item action '");
-              fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, thread->cache_action->name_action.string, data->context.set.title.after->string);
+              fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+              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) {
-                fprintf(data->output.stream, f_string_space_s);
-                fprintf(data->output.stream, "%s", data->context.set.notable.before->string);
-                controller_entry_action_parameters_print(data->output.stream, actions->array[cache->ats.array[at_j]]);
-                fprintf(data->output.stream, "%s", data->context.set.notable.after->string);
+                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]]);
+                fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.after->string);
               }
 
-              fprintf(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", data->error.context.before->string, 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", 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]);
             }
             else if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) {
 
-              if (data->error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data->error.to.stream, "%s%sThe entry item action '", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
-                fprintf(data->error.to.stream, "%s%s%s", data->error.context.after->string, data->error.notable.before->string, thread->cache_action->name_action.string);
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                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) {
-                  fprintf(data->error.to.stream, f_string_space_s);
-                  controller_entry_action_parameters_print(data->error.to.stream, actions->array[cache->ats.array[at_j]]);
+                  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]]);
                 }
 
-                fprintf(data->error.to.stream, "%s%s' is ", data->error.notable.after->string, data->error.context.before->string);
-                fprintf(data->error.to.stream, "%s%srequired%s", data->error.context.after->string, data->error.notable.before->string, data->error.notable.after->string);
-                fprintf(data->error.to.stream, "%s and is in a ", data->error.context.before->string);
-                fprintf(data->error.to.stream, "%s%sfailed%s", data->error.context.after->string, data->error.notable.before->string, data->error.notable.after->string);
-                fprintf(data->error.to.stream, "%s state, skipping execution.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%s' is ", thread_data.data->error.notable.after->string, thread_data.data->error.context.before->string);
+                fprintf(thread_data.data->error.to.stream, "%s%srequired%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s and is in a ", thread_data.data->error.context.before->string);
+                fprintf(thread_data.data->error.to.stream, "%s%sfailed%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s state, skipping execution.%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(data->error, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->error, cache->action);
 
               return F_status_is_error(F_require);
             }
-            else if (data->warning.verbosity == f_console_verbosity_debug) {
-              fprintf(data->warning.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->warning.to.stream, "%s%sThe entry item action '", data->warning.context.before->string, data->warning.prefix ? data->warning.prefix : f_string_empty_s);
-              fprintf(data->warning.to.stream, "%s%s%s", data->warning.context.after->string, data->warning.notable.before->string, thread->cache_action->name_action.string);
+            else if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              fprintf(thread_data.data->warning.to.stream, "%c", f_string_eol_s[0]);
+              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) {
-                fprintf(data->warning.to.stream, f_string_space_s);
-                controller_entry_action_parameters_print(data->warning.to.stream, actions->array[cache->ats.array[at_j]]);
+                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]]);
               }
 
-              fprintf(data->warning.to.stream, "%s%s' is ", data->warning.notable.after->string, data->warning.context.before->string);
-              fprintf(data->warning.to.stream, "%s%srequired%s", data->warning.context.after->string, data->warning.notable.before->string, data->warning.notable.after->string);
-              fprintf(data->warning.to.stream, "%s and is in a ", data->warning.context.before->string);
-              fprintf(data->warning.to.stream, "%s%sfailed%s", data->warning.context.after->string, data->warning.notable.before->string, data->warning.notable.after->string);
-              fprintf(data->warning.to.stream, "%s state, skipping execution.%s%c", data->warning.context.before->string, data->warning.context.after->string, f_string_eol_s[0]);
+              fprintf(thread_data.data->warning.to.stream, "%s%s' is ", thread_data.data->warning.notable.after->string, thread_data.data->warning.context.before->string);
+              fprintf(thread_data.data->warning.to.stream, "%s%srequired%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s and is in a ", thread_data.data->warning.context.before->string);
+              fprintf(thread_data.data->warning.to.stream, "%s%sfailed%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s state, skipping execution.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
 
-              controller_entry_error_print(data->warning, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->warning, cache->action);
             }
           }
           else {
             if (simulate) {
-              fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->output.stream, "The entry item action '");
-              fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, thread->cache_action->name_action.string, data->context.set.title.after->string);
+              fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+              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) {
-                fprintf(data->output.stream, f_string_space_s);
-                fprintf(data->output.stream, "%s", data->context.set.notable.before->string);
-                controller_entry_action_parameters_print(data->output.stream, actions->array[cache->ats.array[at_j]]);
-                fprintf(data->output.stream, "%s", data->context.set.notable.after->string);
+                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]]);
+                fprintf(thread_data.data->output.stream, "%s", thread_data.data->context.set.notable.after->string);
               }
 
-              fprintf(data->output.stream, "' is in a %sfailed%s state, skipping.%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+              fprintf(thread_data.data->output.stream, "' is in a %sfailed%s state, skipping.%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
             }
-            else if (data->warning.verbosity == f_console_verbosity_debug) {
-              fprintf(data->warning.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->warning.to.stream, "%s%sThe entry item action '", data->warning.context.before->string, data->warning.prefix ? data->warning.prefix : f_string_empty_s);
-              fprintf(data->warning.to.stream, "%s%s", data->warning.notable.before->string, thread->cache_action->name_action.string);
+            else if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              fprintf(thread_data.data->warning.to.stream, "%c", f_string_eol_s[0]);
+              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) {
-                fprintf(data->warning.to.stream, f_string_space_s);
-                controller_entry_action_parameters_print(data->warning.to.stream, actions->array[cache->ats.array[at_j]]);
+                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]]);
               }
 
-              fprintf(data->warning.to.stream, "%s' is in a ", data->warning.notable.after->string);
-              fprintf(data->warning.to.stream, "%s%sfailed%s", data->warning.context.after->string, data->warning.notable.before->string, data->warning.notable.after->string);
-              fprintf(data->warning.to.stream, "%s state, skipping.%s%c", data->warning.context.before->string, data->warning.context.after->string, f_string_eol_s[0]);
+              fprintf(thread_data.data->warning.to.stream, "%s' is in a ", thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s%sfailed%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s state, skipping.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
 
-              controller_entry_error_print(data->warning, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->warning, cache->action);
             }
           }
 
@@ -745,41 +741,41 @@ extern "C" {
 
         if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) {
 
-          if (setting->ready == controller_setting_ready_wait) {
+          if (thread_data.setting->ready == controller_setting_ready_wait) {
 
             if (simulate) {
-              fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->output.stream, "Processing entry item action '");
-              fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, controller_string_ready_s, data->context.set.title.after->string);
-              fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->output.stream, "Processing entry item action '");
+              fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, controller_string_ready_s, thread_data.data->context.set.title.after->string);
+              fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]);
             }
             else {
-              controller_perform_ready(*data, setting, cache);
+              controller_perform_ready(thread_data, cache);
               if (F_status_is_error(status)) return status;
             }
 
-            setting->ready = controller_setting_ready_yes;
+            thread_data.setting->ready = controller_setting_ready_yes;
           }
           else if (simulate) {
-            fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-            fprintf(data->output.stream, "Ignoring entry item action '");
-            fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, controller_string_ready_s, data->context.set.title.after->string);
-            fprintf(data->output.stream, "', state already is ready.%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "Ignoring entry item action '");
+            fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, controller_string_ready_s, thread_data.data->context.set.title.after->string);
+            fprintf(thread_data.data->output.stream, "', state already is ready.%c", f_string_eol_s[0]);
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
 
-          if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].number >= setting->entry.items.used) {
+          if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].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 (data->error.verbosity != f_console_verbosity_quiet) {
-              fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->error.to.stream, "%s%sInvalid entry item index ", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
-              fprintf(data->error.to.stream, "%s%s%llu%s", data->error.context.after->string, data->error.notable.before->string, actions->array[cache->ats.array[at_j]].number, data->error.notable.after->string);
-              fprintf(data->error.to.stream, "%s detected.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+            if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              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 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(data->error, *thread->cache_action);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             return F_status_is_error(F_critical);
           }
@@ -787,8 +783,8 @@ extern "C" {
           f_macro_array_lengths_t_increase_by(status, cache->ats, controller_default_allocation_step)
 
           if (F_status_is_error(status)) {
-            fll_error_print(data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
-            controller_entry_error_print(data->error, *thread->cache_action);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             return status;
           }
@@ -802,26 +798,26 @@ extern "C" {
 
           cache->ats.used += 2;
 
-          thread->cache_action->name_action.used = 0;
-          thread->cache_action->line_action = 0;
+          cache->action.name_action.used = 0;
+          cache->action.line_action = 0;
 
-          thread->cache_action->name_item.used = 0;
-          thread->cache_action->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
+          cache->action.name_item.used = 0;
+          cache->action.line_item = thread_data.setting->entry.items.array[cache->ats.array[at_i]].line;
 
-          status = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &thread->cache_action->name_item);
+          status = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
-            controller_entry_error_print(data->error, *thread->cache_action);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             return status;
           }
 
           if (simulate) {
-            fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-            fprintf(data->output.stream, "Processing entry item '");
-            fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, thread->cache_action->name_item.string, data->context.set.title.after->string);
-            fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "Processing entry item '");
+            fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, cache->action.name_item.string, thread_data.data->context.set.title.after->string);
+            fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]);
           }
 
           // exit inner loop to force restarting and start processing the requested item.
@@ -829,16 +825,16 @@ extern "C" {
         }
         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) {
 
-          status = controller_rules_increase(&setting->rules);
+          status = controller_rules_increase(&thread_data.setting->rules);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data->error, F_status_set_fine(status), "controller_rules_increase", F_true);
-            controller_entry_error_print(data->error, *thread->cache_action);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_rules_increase", F_true);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             return status;
           }
 
-          const f_string_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;
+          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);
 
@@ -848,79 +844,91 @@ extern "C" {
           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;
 
-          at = controller_rule_find_loaded(*data, *setting, rule_id);
+          f_thread_mutex_lock(&thread_data.thread->mutex.rule);
+
+          at = controller_rule_find_loaded(rule_id, thread_data);
+
+          if (at < thread_data.setting->rules.used) {
+            f_thread_mutex_unlock(&thread_data.thread->mutex.rule);
+          }
 
           if (simulate) {
-            fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-            fprintf(data->output.stream, "%s entry item rule '", actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule ? "Processing" : "Considering");
-            fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, rule_id.string, data->context.set.title.after->string);
-            fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
+            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, "'.%c", f_string_eol_s[0]);
           }
 
           // the rule is not yet loaded, ensure that it is loaded.
-          if (at == setting->rules.used) {
+          if (at == thread_data.setting->rules.used) {
 
             // rule execution will re-use the existing cache, so save the current cache.
-            const f_array_length_t cache_line_action = thread->cache_action->line_action;
-            const f_array_length_t cache_line_item = thread->cache_action->line_item;
+            const f_array_length_t cache_line_action = cache->action.line_action;
+            const f_array_length_t cache_line_item = cache->action.line_item;
 
-            const f_string_length_t cache_name_action_used = thread->cache_action->name_action.used;
-            const f_string_length_t cache_name_item_used = thread->cache_action->name_item.used;
-            const f_string_length_t cache_name_file_used = thread->cache_action->name_file.used;
+            const f_array_length_t cache_name_action_used = cache->action.name_action.used;
+            const f_array_length_t cache_name_item_used = cache->action.name_item.used;
+            const f_array_length_t cache_name_file_used = cache->action.name_file.used;
 
             char cache_name_action[cache_name_action_used];
             char cache_name_item[cache_name_item_used];
             char cache_name_file[cache_name_file_used];
 
-            memcpy(cache_name_action, thread->cache_action->name_action.string, thread->cache_action->name_action.used);
-            memcpy(cache_name_item, thread->cache_action->name_item.string, thread->cache_action->name_item.used);
-            memcpy(cache_name_file, thread->cache_action->name_file.string, thread->cache_action->name_file.used);
+            memcpy(cache_name_action, cache->action.name_action.string, cache->action.name_action.used);
+            memcpy(cache_name_item, cache->action.name_item.string, cache->action.name_item.used);
+            memcpy(cache_name_file, cache->action.name_file.string, cache->action.name_file.used);
 
-            status = controller_rule_read(*data, *setting, rule_id, thread, cache, &setting->rules.array[setting->rules.used]);
+            status = controller_rule_read(rule_id, thread_data, cache, &thread_data.setting->rules.array[thread_data.setting->rules.used]);
 
             // restore cache.
-            memcpy(thread->cache_action->name_action.string, cache_name_action, cache_name_action_used);
-            memcpy(thread->cache_action->name_item.string, cache_name_item, cache_name_item_used);
-            memcpy(thread->cache_action->name_file.string, cache_name_file, cache_name_file_used);
+            memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
+            memcpy(cache->action.name_item.string, cache_name_item, cache_name_item_used);
+            memcpy(cache->action.name_file.string, cache_name_file, cache_name_file_used);
 
-            thread->cache_action->name_action.string[cache_name_action_used] = 0;
-            thread->cache_action->name_item.string[cache_name_item_used] = 0;
-            thread->cache_action->name_file.string[cache_name_file_used] = 0;
+            cache->action.name_action.string[cache_name_action_used] = 0;
+            cache->action.name_item.string[cache_name_item_used] = 0;
+            cache->action.name_file.string[cache_name_file_used] = 0;
 
-            thread->cache_action->name_action.used = cache_name_action_used;
-            thread->cache_action->name_item.used = cache_name_item_used;
-            thread->cache_action->name_file.used = cache_name_file_used;
+            cache->action.name_action.used = cache_name_action_used;
+            cache->action.name_item.used = cache_name_item_used;
+            cache->action.name_file.used = cache_name_file_used;
 
-            thread->cache_action->line_action = cache_line_action;
-            thread->cache_action->line_item = cache_line_item;
+            cache->action.line_action = cache_line_action;
+            cache->action.line_item = cache_line_item;
 
             if (F_status_is_error(status)) {
-              controller_entry_error_print(data->error, *thread->cache_action);
+              controller_entry_error_print(thread_data.data->error, cache->action);
 
-              if (!simulate) break;
+              if (!simulate) {
+                f_thread_mutex_unlock(&thread_data.thread->mutex.rule);
+
+                break;
+              }
             }
             else {
-              setting->rules.used++;
+              thread_data.setting->rules.used++;
             }
+
+            f_thread_mutex_unlock(&thread_data.thread->mutex.rule);
           }
 
           if (F_status_is_error_not(status)) {
 
             // rule execution will re-use the existing cache, so save the current cache.
-            const f_array_length_t cache_line_action = thread->cache_action->line_action;
-            const f_array_length_t cache_line_item = thread->cache_action->line_item;
+            const f_array_length_t cache_line_action = cache->action.line_action;
+            const f_array_length_t cache_line_item = cache->action.line_item;
 
-            const f_string_length_t cache_name_action_used = thread->cache_action->name_action.used;
-            const f_string_length_t cache_name_item_used = thread->cache_action->name_item.used;
-            const f_string_length_t cache_name_file_used = thread->cache_action->name_file.used;
+            const f_array_length_t cache_name_action_used = cache->action.name_action.used;
+            const f_array_length_t cache_name_item_used = cache->action.name_item.used;
+            const f_array_length_t cache_name_file_used = cache->action.name_file.used;
 
             char cache_name_action[cache_name_action_used];
             char cache_name_item[cache_name_item_used];
             char cache_name_file[cache_name_file_used];
 
-            memcpy(cache_name_action, thread->cache_action->name_action.string, thread->cache_action->name_action.used);
-            memcpy(cache_name_item, thread->cache_action->name_item.string, thread->cache_action->name_item.used);
-            memcpy(cache_name_file, thread->cache_action->name_file.string, thread->cache_action->name_file.used);
+            memcpy(cache_name_action, cache->action.name_action.string, cache->action.name_action.used);
+            memcpy(cache_name_item, cache->action.name_item.string, cache->action.name_item.used);
+            memcpy(cache_name_file, cache->action.name_file.string, cache->action.name_file.used);
 
             if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
               rule_options = 0;
@@ -937,42 +945,45 @@ extern "C" {
                 rule_options |= controller_rule_option_wait;
               }
 
-              f_thread_mutex_lock(&thread->setting->rules.array[at].lock);
+              // @fixme this logic needs to change.
+              //        determine how to pass rule ids.
+              //        each thread has a main rule, but child threads need to be copied over and passed somehow.
+              f_thread_mutex_lock(&thread_data.setting->rules.array[at].lock);
 
               if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_asynchronous) {
                 rule_options |= controller_rule_option_asynchronous;
 
-                status = controller_rule_process_asynchronous(at, controller_rule_action_type_start, rule_options, thread);
+                status = controller_rule_process_asynchronous(at, controller_rule_action_type_start, rule_options, thread_data, cache);
               }
               else {
-                status = controller_rule_process(at, controller_rule_action_type_start, rule_options, thread, 0);
+                status = controller_rule_process(at, controller_rule_action_type_start, rule_options, thread_data, cache);
               }
 
               if (status == F_child || status == F_signal) break;
 
-              f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-              f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+              f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+              f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
             }
 
             // restore cache.
-            memcpy(thread->cache_action->name_action.string, cache_name_action, cache_name_action_used);
-            memcpy(thread->cache_action->name_item.string, cache_name_item, cache_name_item_used);
-            memcpy(thread->cache_action->name_file.string, cache_name_file, cache_name_file_used);
+            memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
+            memcpy(cache->action.name_item.string, cache_name_item, cache_name_item_used);
+            memcpy(cache->action.name_file.string, cache_name_file, cache_name_file_used);
 
-            thread->cache_action->name_action.string[cache_name_action_used] = 0;
-            thread->cache_action->name_item.string[cache_name_item_used] = 0;
-            thread->cache_action->name_file.string[cache_name_file_used] = 0;
+            cache->action.name_action.string[cache_name_action_used] = 0;
+            cache->action.name_item.string[cache_name_item_used] = 0;
+            cache->action.name_file.string[cache_name_file_used] = 0;
 
-            thread->cache_action->name_action.used = cache_name_action_used;
-            thread->cache_action->name_item.used = cache_name_item_used;
-            thread->cache_action->name_file.used = cache_name_file_used;
+            cache->action.name_action.used = cache_name_action_used;
+            cache->action.name_item.used = cache_name_item_used;
+            cache->action.name_file.used = cache_name_file_used;
 
-            thread->cache_action->line_action = cache_line_action;
-            thread->cache_action->line_item = cache_line_item;
+            cache->action.line_action = cache_line_action;
+            cache->action.line_item = cache_line_item;
           }
 
           if (F_status_is_error(status)) {
-            controller_entry_error_print(data->error, *thread->cache_action);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             if (!simulate || F_status_set_fine(status) == F_memory_not) {
               break;
@@ -994,66 +1005,66 @@ extern "C" {
               code = controller_string_stop_s;
             }
 
-            fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-            fprintf(data->output.stream, "Processing entry item action '");
-            fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, controller_string_timeout_s, data->context.set.title.after->string);
-            fprintf(data->output.stream, "' setting '");
-            fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, code, data->context.set.important.after->string);
-            fprintf(data->output.stream, "' to '");
-            fprintf(data->output.stream, "%s%llu%s", data->context.set.important.before->string, actions->array[cache->ats.array[at_j]].number, data->context.set.important.after->string);
-            fprintf(data->output.stream, "' MegaTime (milliseconds).%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->output.stream, "Processing entry item action '");
+            fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, controller_string_timeout_s, thread_data.data->context.set.title.after->string);
+            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, "' MegaTime (milliseconds).%c", f_string_eol_s[0]);
           }
 
           if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_kill) {
-            setting->timeout_kill = actions->array[cache->ats.array[at_j]].number;
+            thread_data.setting->timeout_kill = actions->array[cache->ats.array[at_j]].number;
           }
           else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_start) {
-            setting->timeout_start = actions->array[cache->ats.array[at_j]].number;
+            thread_data.setting->timeout_start = actions->array[cache->ats.array[at_j]].number;
           }
           else if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_stop) {
-            setting->timeout_stop = actions->array[cache->ats.array[at_j]].number;
+            thread_data.setting->timeout_stop = actions->array[cache->ats.array[at_j]].number;
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_failsafe) {
 
-          if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].number >= setting->entry.items.used) {
+          if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].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 (data->error.verbosity != f_console_verbosity_quiet) {
-              fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->error.to.stream, "%s%sInvalid entry item index ", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
-              fprintf(data->error.to.stream, "%s%s%llu%s", data->error.context.after->string, data->error.notable.before->string, actions->array[cache->ats.array[at_j]].number, data->error.notable.after->string);
-              fprintf(data->error.to.stream, "%s detected.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
+            if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              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 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(data->error, *thread->cache_action);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             return F_status_is_error(F_critical);
           }
           else {
-            setting->failsafe_enabled = F_true;
-            setting->failsafe_rule_id = actions->array[cache->ats.array[at_j]].number;
+            thread_data.setting->failsafe_enabled = F_true;
+            thread_data.setting->failsafe_rule_id = actions->array[cache->ats.array[at_j]].number;
 
             if (simulate) {
-              fprintf(data->output.stream, "%c", f_string_eol_s[0]);
-              fprintf(data->output.stream, "Processing entry item action '");
-              fprintf(data->output.stream, "%s%s%s", data->context.set.title.before->string, controller_string_failsafe_s, data->context.set.title.after->string);
-              fprintf(data->output.stream, "' setting value to '");
-              fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, setting->entry.items.array[setting->failsafe_rule_id].name.string, data->context.set.important.after->string);
-              fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->output.stream, "Processing entry item action '");
+              fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, controller_string_failsafe_s, thread_data.data->context.set.title.after->string);
+              fprintf(thread_data.data->output.stream, "' setting value to '");
+              fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.important.before->string, thread_data.setting->entry.items.array[thread_data.setting->failsafe_rule_id].name.string, thread_data.data->context.set.important.after->string);
+              fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]);
             }
           }
         }
       } // for
 
-      if (setting->signal) {
+      if (thread_data.setting->signal) {
         status = F_signal;
       }
 
       if (status == F_child || status == F_signal) break;
 
-      thread->cache_action->line_action = 0;
-      thread->cache_action->name_action.used = 0;
+      cache->action.line_action = 0;
+      cache->action.name_action.used = 0;
 
       if (F_status_is_error(status)) {
         if (!simulate || F_status_set_fine(status) == F_memory_not) {
@@ -1073,14 +1084,14 @@ extern "C" {
         cache->ats.used -= 2;
         cache->ats.array[at_j]++;
 
-        thread->cache_action->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
-        thread->cache_action->name_item.used = 0;
+        cache->action.line_item = thread_data.setting->entry.items.array[cache->ats.array[at_i]].line;
+        cache->action.name_item.used = 0;
 
-        status = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &thread->cache_action->name_item);
+        status = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
         if (F_status_is_error(status)) {
-          fll_error_print(data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
-          controller_entry_error_print(data->error, *thread->cache_action);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(thread_data.data->error, cache->action);
 
           break;
         }
@@ -1092,7 +1103,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status) && simulate) {
-      fprintf(data->output.stream, "%c", f_string_eol_s[0]);
+      fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
     }
 
     return status;
@@ -1144,7 +1155,7 @@ extern "C" {
       if (status == F_false) return F_false;
     }
 
-    for (f_string_length_t i = f_macro_utf_byte_width(name.string[0]); i < name.used; i += f_macro_utf_byte_width(name.string[i])) {
+    for (f_array_length_t i = f_macro_utf_byte_width(name.string[0]); i < name.used; i += f_macro_utf_byte_width(name.string[i])) {
 
       if (name.string[i] == '_') continue;
 
@@ -1165,7 +1176,7 @@ extern "C" {
 
     f_status_t status = F_none;
 
-    for (f_string_length_t i = 0; i < name.used; i += f_macro_utf_byte_width(name.string[i])) {
+    for (f_array_length_t i = 0; i < name.used; i += f_macro_utf_byte_width(name.string[i])) {
 
       status = f_utf_is_graph(name.string, name.used);
 
index a173406c398a809be97d8b47f17168355900bcae..38eb59a2d80baf25e40222732675ecf7fc192479 100644 (file)
@@ -80,10 +80,6 @@ extern "C" {
 /**
  * Load a file from the controller settings directory.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
  * @param path_prefix
  *   The path prefix, such as 'entries' from '/etc/controller/entries/default.entry'.
  * @param path_name
@@ -94,6 +90,8 @@ extern "C" {
  *   The length of the prefix path.
  * @param path_suffix_length
  *   The length of the suffix path.
+ * @param thread_data
+ *   The thread data.
  * @param cache
  *   The following within the cache is updated:
  *   - name_file: The partial path of the file is inserted.
@@ -116,7 +114,7 @@ extern "C" {
  * @see f_string_dynamic_terminate_after()
  */
 #ifndef _di_controller_file_load_
-  extern f_status_t controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_file_load(const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_array_length_t path_prefix_length, const f_array_length_t path_suffix_length, controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_file_load_
 
 /**
@@ -217,10 +215,8 @@ extern "C" {
  *
  * This prints messages on errors.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
+ * @param thread_data
+ *   The thread data.
  * @param cache
  *   The cache.
  *
@@ -232,16 +228,16 @@ extern "C" {
  * @see controller_file_pid_create()
  */
 #ifndef _di_controller_perform_ready_
-  extern f_status_t controller_perform_ready(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_perform_ready(controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_perform_ready_
 
 /**
  * Pre-process all items for the loaded entry.
  *
+ * @param thread_data
+ *   The thread data.
  * @param cache
  *   The main/global cache to use.
- * @param thread
- *   The thread data.
  *
  * @return
  *   F_none on success.
@@ -260,16 +256,16 @@ extern "C" {
  * @see f_string_dynamic_terminate_after()
  */
 #ifndef _di_controller_preprocess_entry_
-  extern f_status_t controller_preprocess_entry(controller_cache_t *cache, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_preprocess_entry(controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_preprocess_entry_
 
 /**
  * Process (execute) all items for the loaded entry.
  *
+ * @param thread_data
+ *   The thread data.
  * @param cache
  *   The main/global cache to use.
- * @param thread
- *   The thread data.
  *
  * @return
  *   F_none on success.
@@ -284,7 +280,7 @@ extern "C" {
  * @see controller_string_dynamic_append_terminated()
  */
 #ifndef _di_controller_process_entry_
-  extern f_status_t controller_process_entry(controller_cache_t *cache, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_process_entry(controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_process_entry_
 
 /**
index 4679ef7d235ebd0f626537a531dc59852bfb1fed..0834af0eeb3a13d53699fa0f0718e5984908dabe 100644 (file)
@@ -91,7 +91,7 @@ extern "C" {
 #endif // _di_controller_entry_actions_increase_by_
 
 #ifndef _di_controller_entry_actions_read_
-  f_status_t controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_cache_t *cache, controller_entry_actions_t *actions) {
+  f_status_t controller_entry_actions_read(const f_string_range_t content_range, controller_thread_data_t thread_data, controller_cache_t *cache, controller_entry_actions_t *actions) {
     f_status_t status = F_none;
     f_status_t status_action = F_none;
 
@@ -119,7 +119,7 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "fll_fss_extended_read", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "fll_fss_extended_read", F_true);
 
       return status;
     }
@@ -127,7 +127,7 @@ extern "C" {
     status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
 
       return status;
     }
@@ -137,7 +137,7 @@ extern "C" {
     status = controller_entry_actions_increase_by(cache->object_actions.used, actions);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "controller_entry_actions_increase_by", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_entry_actions_increase_by", F_true);
 
       return status;
     }
@@ -167,7 +167,7 @@ extern "C" {
       status = f_fss_count_lines(cache->buffer_file, cache->object_actions.array[i].start, &cache->action.line_action);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
         break;
       }
 
@@ -176,7 +176,7 @@ extern "C" {
       status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_actions.array[i], &cache->action.name_action);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
         break;
       }
 
@@ -199,14 +199,14 @@ extern "C" {
         actions->array[actions->used].type = controller_entry_action_type_timeout;
       }
       else {
-        if (data.warning.verbosity == f_console_verbosity_debug) {
-          fprintf(data.warning.to.stream, "%s%sUnknown entry item action '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-          fprintf(data.warning.to.stream, "%s%s", data.warning.context.after->string, data.warning.notable.before->string);
-          f_print_dynamic(data.warning.to.stream, cache->action.name_action);
-          fprintf(data.warning.to.stream, "%s", data.warning.notable.after->string);
-          fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
-
-          controller_entry_error_print(data.warning, cache->action);
+        if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+          fprintf(thread_data.data->warning.to.stream, "%s%sUnknown 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.context.after->string, thread_data.data->warning.notable.before->string);
+          f_print_dynamic(thread_data.data->warning.to.stream, cache->action.name_action);
+          fprintf(thread_data.data->warning.to.stream, "%s", thread_data.data->warning.notable.after->string);
+          fprintf(thread_data.data->warning.to.stream, "%s'.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
+
+          controller_entry_error_print(thread_data.data->warning, cache->action);
         }
 
         continue;
@@ -237,15 +237,15 @@ extern "C" {
       if (cache->content_actions.array[i].used < at_least || cache->content_actions.array[i].used > at_most) {
         action->status = F_status_set_error(F_parameter);
 
-        if (data.error.verbosity != f_console_verbosity_quiet) {
-          fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(data.error.to.stream, "%s%sThe entry item action '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-          fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, cache->action.name_action.string, data.error.notable.after->string);
-          fprintf(data.error.to.stream, "%s' requires ", data.error.context.before->string);
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+          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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, cache->action.name_action.string, thread_data.data->error.notable.after->string);
+          fprintf(thread_data.data->error.to.stream, "%s' requires ", thread_data.data->error.context.before->string);
 
           if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) {
-            fprintf(data.error.to.stream, "%s%s%llu%s", data.error.context.after->string, data.error.notable.before->string, cache->action.line_action, data.error.notable.after->string);
-            fprintf(data.error.to.stream, "%s or more parameters.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+            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, cache->action.line_action, thread_data.data->error.notable.after->string);
+            fprintf(thread_data.data->error.to.stream, "%s or more parameters.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
           }
           else {
             uint8_t parameters = 0;
@@ -257,9 +257,9 @@ extern "C" {
               parameters = 2;
             }
 
-            fprintf(data.error.to.stream, "exactly ", data.error.context.before->string);
-            fprintf(data.error.to.stream, "%s%s%u%s", data.error.context.after->string, data.error.notable.before->string, parameters, data.error.notable.after->string);
-            fprintf(data.error.to.stream, "%s parameters.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+            fprintf(thread_data.data->error.to.stream, "exactly ", thread_data.data->error.context.before->string);
+            fprintf(thread_data.data->error.to.stream, "%s%s%u%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, parameters, thread_data.data->error.notable.after->string);
+            fprintf(thread_data.data->error.to.stream, "%s parameters.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
           }
         }
       }
@@ -279,7 +279,7 @@ extern "C" {
         status = f_string_dynamics_increase_by(allocate, &action->parameters);
 
         if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true);
 
           action->status = status;
 
@@ -297,7 +297,7 @@ extern "C" {
           status = f_string_dynamic_partial_append_nulless(cache->buffer_file, cache->content_actions.array[i].array[j], &action->parameters.array[j]);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
 
             action->status = status;
 
@@ -319,7 +319,7 @@ extern "C" {
               status = fll_path_canonical(action->parameters.array[0].string, &cache->buffer_path);
 
               if (F_status_is_error(status)) {
-                fll_error_print(data.error, F_status_set_fine(status), "fll_path_canonical", F_true);
+                fll_error_print(thread_data.data->error, F_status_set_fine(status), "fll_path_canonical", F_true);
 
                 action->status = status;
 
@@ -340,9 +340,9 @@ extern "C" {
                 status_action = action->status;
               }
 
-              if (data.error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.error.to.stream, "%s%sThe entry item action must not have an empty string for a path (the first parameter).%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, data.error.context.after->string, f_string_eol_s[0]);
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action must not have an empty string for a path (the first parameter).%s%c", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s, thread_data.data->error.context.after->string, f_string_eol_s[0]);
               }
             }
 
@@ -352,7 +352,7 @@ extern "C" {
               status = f_file_name_base(action->parameters.array[1].string, action->parameters.array[1].used, &cache->buffer_path);
 
               if (F_status_is_error(status)) {
-                fll_error_print(data.error, F_status_set_fine(status), "f_file_name_base", F_true);
+                fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_file_name_base", F_true);
 
                 if (F_status_set_fine(status) == F_memory_not) {
                   status_action = status;
@@ -368,11 +368,11 @@ extern "C" {
               else {
                 if (fl_string_dynamic_compare(action->parameters.array[1], cache->buffer_path) == F_equal_to_not) {
 
-                  if (data.error.verbosity != f_console_verbosity_quiet) {
+                  if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
                     status = f_string_dynamic_terminate_after(&cache->buffer_path);
 
                     if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+                      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
 
                       action->status = status;
 
@@ -383,12 +383,12 @@ extern "C" {
                       break;
                     }
 
-                    fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                    fprintf(data.error.to.stream, "%s%sThe entry item action second parameter '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                    fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[1].string, data.error.notable.after->string);
-                    fprintf(data.error.to.stream, "%s' must be a base path name, such as '", data.error.context.before->string);
-                    fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, cache->buffer_path.string, data.error.notable.after->string);
-                    fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+                    fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                    fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action second parameter '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, action->parameters.array[1].string, thread_data.data->error.notable.after->string);
+                    fprintf(thread_data.data->error.to.stream, "%s' must be a base path name, such as '", thread_data.data->error.context.before->string);
+                    fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, cache->buffer_path.string, thread_data.data->error.notable.after->string);
+                    fprintf(thread_data.data->error.to.stream, "%s'.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
                   }
 
                   action->status = F_status_set_error(F_parameter);
@@ -406,9 +406,9 @@ extern "C" {
                 status_action = action->status;
               }
 
-              if (data.error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.error.to.stream, "%s%sThe entry item action must not have an empty string for a rule name (the second parameter).%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, data.error.context.after->string, f_string_eol_s[0]);
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action must not have an empty string for a rule name (the second parameter).%s%c", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s, thread_data.data->error.context.after->string, f_string_eol_s[0]);
               }
             }
 
@@ -432,17 +432,17 @@ extern "C" {
                   }
                 }
 
-                if (data.error.verbosity != f_console_verbosity_quiet) {
-                  fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                  fprintf(data.error.to.stream, "%s%sThe entry item action third parameter (and beyond) must be one of '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_asynchronous_s, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s', '", data.error.context.before->string);
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_require_s, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s', or '", data.error.context.before->string);
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_wait_s, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s' but instead has '", data.error.context.before->string);
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[j].string, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+                if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                  fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                  fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action third parameter (and beyond) must be one of '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_asynchronous_s, thread_data.data->error.notable.after->string);
+                  fprintf(thread_data.data->error.to.stream, "%s', '", thread_data.data->error.context.before->string);
+                  fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_require_s, thread_data.data->error.notable.after->string);
+                  fprintf(thread_data.data->error.to.stream, "%s', or '", thread_data.data->error.context.before->string);
+                  fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_wait_s, thread_data.data->error.notable.after->string);
+                  fprintf(thread_data.data->error.to.stream, "%s' but instead has '", thread_data.data->error.context.before->string);
+                  fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, action->parameters.array[j].string, thread_data.data->error.notable.after->string);
+                  fprintf(thread_data.data->error.to.stream, "%s'.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
                 }
               }
             } // for
@@ -455,11 +455,11 @@ extern "C" {
                 status_action = action->status;
               }
 
-              if (data.error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.error.to.stream, "%s%sThe entry item action may not specify the reserved item '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_main_s, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action may not specify the reserved item '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_main_s, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s'.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
               }
             }
           }
@@ -481,17 +481,17 @@ extern "C" {
                 status_action = action->status;
               }
 
-              if (data.error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.error.to.stream, "%s%sThe entry item action must have one of '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_kill_s, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s', '", data.error.context.before->string);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_start_s, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s', or '", data.error.context.before->string);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_stop_s, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s' but instead has '", data.error.context.before->string);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[0].string, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action must have one of '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_kill_s, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s', '", thread_data.data->error.context.before->string);
+                fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_start_s, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s', or '", thread_data.data->error.context.before->string);
+                fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_stop_s, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s' but instead has '", thread_data.data->error.context.before->string);
+                fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, action->parameters.array[0].string, thread_data.data->error.notable.after->string);
+                fprintf(thread_data.data->error.to.stream, "%s'.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
               }
             }
 
@@ -511,17 +511,17 @@ extern "C" {
                 }
 
                 if (F_status_set_fine(status) == F_memory_not) {
-                  fll_error_print(data.error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true);
+                  fll_error_print(thread_data.data->error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true);
 
                   status_action = status;
                   break;
                 }
 
-                if (data.error.verbosity != f_console_verbosity_quiet) {
-                  fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                  fprintf(data.error.to.stream, "%s%sThe entry item action parameter '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[1].string, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s' is not a valid supported number.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+                if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                  fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                  fprintf(thread_data.data->error.to.stream, "%s%sThe entry item action parameter '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, action->parameters.array[1].string, thread_data.data->error.notable.after->string);
+                  fprintf(thread_data.data->error.to.stream, "%s' is not a valid supported number.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
                 }
               }
             }
@@ -594,11 +594,14 @@ extern "C" {
 #endif // _di_controller_entry_items_increase_by_
 
 #ifndef _di_controller_entry_read_
-  f_status_t controller_entry_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_cache_t *cache, controller_entry_t *entry) {
+  f_status_t controller_entry_read(const f_string_static_t entry_name, controller_thread_data_t thread_data, controller_cache_t *cache) {
     f_status_t status = F_none;
 
-    entry->status = F_known_not;
-    entry->items.used = 0;
+    // @fixme all printfs in this function and child functions now need to be using the print mutex.
+    // @fixme this should lock the global rule mutex until all rules for the entry have been processed.
+
+    thread_data.setting->entry.status = F_known_not;
+    thread_data.setting->entry.items.used = 0;
 
     cache->action.line_action = 0;
     cache->action.line_item = 0;
@@ -635,7 +638,7 @@ extern "C" {
     cache->action.name_action.used = 0;
     cache->action.name_item.used = 0;
 
-    status = controller_file_load(data, setting, controller_string_entries_s, entry_name, controller_string_entry_s, controller_string_entries_length, controller_string_entry_length, cache);
+    status = controller_file_load(controller_string_entries_s, entry_name, controller_string_entry_s, controller_string_entries_length, controller_string_entry_length, thread_data, cache);
 
     if (F_status_is_error_not(status)) {
       if (cache->buffer_file.used) {
@@ -644,20 +647,20 @@ extern "C" {
         status = fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments);
 
         if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
         }
         else {
           status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
           }
         }
       }
       else {
-        if (data.error.verbosity != f_console_verbosity_quiet) {
-          fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(data.error.to.stream, "%s%sThe entry file is empty.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, data.error.context.after->string, f_string_eol_s[0]);
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+          fprintf(thread_data.data->error.to.stream, "%s%sThe entry file is empty.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.prefix ? thread_data.data->error.prefix : f_string_empty_s, thread_data.data->error.context.after->string, f_string_eol_s[0]);
         }
 
         status = F_status_set_error(F_data_not);
@@ -665,10 +668,10 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status) && cache->object_items.used) {
-      status = controller_entry_items_increase_by(cache->object_items.used, &entry->items);
+      status = controller_entry_items_increase_by(cache->object_items.used, &thread_data.setting->entry.items);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true);
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true);
       }
       else {
 
@@ -683,7 +686,7 @@ extern "C" {
 
         for (; i < cache->object_items.used; ++i) {
 
-          if (setting.signal) {
+          if (thread_data.setting->signal) {
             return F_signal;
           }
 
@@ -710,39 +713,39 @@ extern "C" {
           cache->action.name_action.used = 0;
           cache->action.name_item.used = 0;
 
-          status = controller_entry_items_increase_by(controller_default_allocation_step, &entry->items);
+          status = controller_entry_items_increase_by(controller_default_allocation_step, &thread_data.setting->entry.items);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true);
             break;
           }
 
           status = controller_string_dynamic_partial_append_terminated(cache->buffer_file, cache->object_items.array[i], &cache->action.name_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_partial_append_terminated", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_partial_append_terminated", F_true);
             break;
           }
 
           status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->action.line_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
             break;
           }
 
           cache->action.line_item++;
 
-          for (j = (code & 0x1) ? 1 : 0; j < entry->items.used; ++j) {
+          for (j = (code & 0x1) ? 1 : 0; j < thread_data.setting->entry.items.used; ++j) {
 
-            if (fl_string_dynamic_compare(entry->items.array[j].name, cache->action.name_item) == F_equal_to) {
-              if (data.warning.verbosity == f_console_verbosity_debug) {
-                fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.warning.to.stream, "%s%sIgnoring duplicate entry item '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-                fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.warning.notable.before->string, cache->action.name_file.string, data.warning.notable.after->string);
-                fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
+            if (fl_string_dynamic_compare(thread_data.setting->entry.items.array[j].name, cache->action.name_item) == F_equal_to) {
+              if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+                fprintf(thread_data.data->warning.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(thread_data.data->warning.to.stream, "%s%sIgnoring duplicate entry item '", 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%s", thread_data.data->warning.context.after->string, thread_data.data->warning.notable.before->string, cache->action.name_file.string, thread_data.data->warning.notable.after->string);
+                fprintf(thread_data.data->warning.to.stream, "%s'.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
 
-                controller_entry_error_print(data.warning, cache->action);
+                controller_entry_error_print(thread_data.data->warning, cache->action);
               }
 
               code |= 0x2;
@@ -759,35 +762,35 @@ extern "C" {
 
             at = 0;
 
-            if (!entry->items.used) {
-              entry->items.used = 1;
+            if (!thread_data.setting->entry.items.used) {
+              thread_data.setting->entry.items.used = 1;
             }
           }
-          else if (entry->items.used) {
-            at = entry->items.used++;
+          else if (thread_data.setting->entry.items.used) {
+            at = thread_data.setting->entry.items.used++;
           }
           else {
 
             // skip position 0, which is reserved for "main".
-            entry->items.array[0].name.used = 0;
+            thread_data.setting->entry.items.array[0].name.used = 0;
 
             at = 1;
-            entry->items.used = 2;
+            thread_data.setting->entry.items.used = 2;
           }
 
-          entry->items.array[at].line = cache->action.line_item;
+          thread_data.setting->entry.items.array[at].line = cache->action.line_item;
 
-          status = controller_string_dynamic_append_terminated(cache->action.name_item, &entry->items.array[at].name);
+          status = controller_string_dynamic_append_terminated(cache->action.name_item, &thread_data.setting->entry.items.array[at].name);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
             break;
           }
 
-          status = controller_entry_actions_read(data, setting, *range, cache, &entry->items.array[at].actions);
+          status = controller_entry_actions_read(*range, thread_data, cache, &thread_data.setting->entry.items.array[at].actions);
 
           if (F_status_is_error(status)) {
-            controller_entry_error_print(data.error, cache->action);
+            controller_entry_error_print(thread_data.data->error, cache->action);
 
             if (F_status_set_fine(status) == F_memory_not) {
               break;
@@ -800,11 +803,11 @@ extern "C" {
           cache->action.name_item.used = 0;
 
           if (!(code & 0x1)) {
-            if (data.error.verbosity != f_console_verbosity_quiet) {
-              fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data.error.to.stream, "%s%sThe required entry item '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-              fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_main_s, data.error.notable.after->string);
-              fprintf(data.error.to.stream, "%s' was not found.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+            if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+              fprintf(thread_data.data->error.to.stream, "%s%sThe required entry item '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_string_main_s, thread_data.data->error.notable.after->string);
+              fprintf(thread_data.data->error.to.stream, "%s' was not found.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
             }
 
             status = F_status_set_error(F_found_not);
@@ -818,15 +821,15 @@ extern "C" {
             // 0x1 = missing or not, 0x2 = one or more missing.
             uint8_t missing = 0;
 
-            for (i = 0; i < entry->items.used; ++i) {
+            for (i = 0; i < thread_data.setting->entry.items.used; ++i) {
 
-              for (j = 0; j < entry->items.array[i].actions.used; ++j) {
+              for (j = 0; j < thread_data.setting->entry.items.array[i].actions.used; ++j) {
 
-                if (setting.signal) {
+                if (thread_data.setting->signal) {
                   return F_signal;
                 }
 
-                action = &entry->items.array[i].actions.array[j];
+                action = &thread_data.setting->entry.items.array[i].actions.array[j];
 
                 // only process actions that don't already have an error.
                 if (F_status_is_error(action->status)) continue;
@@ -834,9 +837,9 @@ extern "C" {
                 if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) {
                   missing |= 0x1;
 
-                  for (k = 0; k < entry->items.used; ++k) {
+                  for (k = 0; k < thread_data.setting->entry.items.used; ++k) {
 
-                    if (fl_string_dynamic_compare(action->parameters.array[0], entry->items.array[k].name) == F_equal_to) {
+                    if (fl_string_dynamic_compare(action->parameters.array[0], thread_data.setting->entry.items.array[k].name) == F_equal_to) {
                       if (missing & 0x1) {
                         missing -= 0x1;
                       }
@@ -849,29 +852,29 @@ extern "C" {
                     missing |= 0x2;
 
                     cache->action.line_action = action->line;
-                    cache->action.line_item = entry->items.array[i].line;
+                    cache->action.line_item = thread_data.setting->entry.items.array[i].line;
 
-                    status = controller_string_dynamic_append_terminated(entry->items.array[i].name, &cache->action.name_item);
+                    status = controller_string_dynamic_append_terminated(thread_data.setting->entry.items.array[i].name, &cache->action.name_item);
 
                     if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+                      fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
                       break;
                     }
 
-                    if (data.error.verbosity != f_console_verbosity_quiet) {
-                      fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                      fprintf(data.error.to.stream, "%s%sThe required entry item '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                      fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, action->parameters.array[0].string, data.error.notable.after->string);
-                      fprintf(data.error.to.stream, "%s' does not exist.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+                    if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                      fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+                      fprintf(thread_data.data->error.to.stream, "%s%sThe required entry item '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, action->parameters.array[0].string, thread_data.data->error.notable.after->string);
+                      fprintf(thread_data.data->error.to.stream, "%s' does not exist.%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(data.error, cache->action);
+                      controller_entry_error_print(thread_data.data->error, cache->action);
                     }
 
                     action->number = 0;
                     action->status = controller_status_simplify(F_found_not);
 
-                    // @fixme review how entry->status is being handled with respect to action->status (here the action failed, should the entire entry fail? at the moment if mode is simulation this prevents simulation from continuing).
-                    //entry->status = controller_status_simplify(F_found_not);
+                    // @fixme review how thread_data.setting->entry.status is being handled with respect to action->status (here the action failed, should the entire entry fail? at the moment if mode is simulation this prevents simulation from continuing).
+                    //thread_data.setting->entry.status = controller_status_simplify(F_found_not);
 
                     cache->action.name_action.used = 0;
                     cache->action.name_item.used = 0;
@@ -885,8 +888,8 @@ extern "C" {
 
             // the error is already fully printed and the entry status is already assigned, so immediately exit.
             if (missing & 0x2) {
-              // @fixme review how entry->status is being handled with respect to action->status (here the action failed, should the entire entry fail? at the moment if mode is simulation this prevents simulation from continuing).
-              //return entry->status;
+              // @fixme review how thread_data.setting->entry.status is being handled with respect to action->status (here the action failed, should the entire entry fail? at the moment if mode is simulation this prevents simulation from continuing).
+              //return thread_data.setting->entry.status;
             }
           }
         }
@@ -894,15 +897,15 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      controller_entry_error_print(data.error, cache->action);
+      controller_entry_error_print(thread_data.data->error, cache->action);
 
-      entry->status = controller_status_simplify(F_status_set_fine(status));
+      thread_data.setting->entry.status = controller_status_simplify(F_status_set_fine(status));
     }
     else {
-      entry->status = F_none;
+      thread_data.setting->entry.status = F_none;
     }
 
-    return entry->status;
+    return thread_data.setting->entry.status;
   }
 #endif // _di_controller_entry_read_
 
index 40d0988a7ea3666c8db4674e6129f928396b0e02..349aa760ab21133fbb858b4b7ad3ecf0ab0df42b 100644 (file)
@@ -63,12 +63,10 @@ extern "C" {
 /**
  * Read the entry list, extracting all items and values.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
  * @param content_range
  *   The range in the list buffer representing the content.
+ * @param thread_data
+ *   The thread data.
  * @param cache
  *   A structure for containing and caching relevant data.
  * @param actions
@@ -96,7 +94,7 @@ extern "C" {
  * @see fll_fss_extended_read()
  */
 #ifndef _di_controller_entry_actions_read_
-  extern f_status_t controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_cache_t *cache, controller_entry_actions_t *actions) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_entry_actions_read(const f_string_range_t content_range, controller_thread_data_t thread_data, controller_cache_t *cache, controller_entry_actions_t *actions) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_entry_actions_read_
 
 /**
@@ -141,18 +139,15 @@ extern "C" {
 /**
  * Read the entry, extracting all lists.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
  * @param entry_name
  *   The string identifying the entry.
  *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
  *   "/etc/controller/entries/example/my.entry" would have a rule id of "example/my".
+ * @param thread_data
+ *   The thread data.
  * @param cache
- *   A structure for containing and caching relevant data.
- * @param entry
- *   The processed entry.
+ *   The cache for the specific thread.
+ *   This should be the cache thread_data.thread->asynchronouss.array[thread_data.id].cache.
  *
  * @return
  *   F_none on success.
@@ -184,7 +179,7 @@ extern "C" {
  * @see fll_fss_basic_list_read()
  */
 #ifndef _di_controller_entry_read_
-  extern f_status_t controller_entry_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_cache_t *cache, controller_entry_t *entry) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_entry_read(const f_string_static_t entry_name, controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_entry_read_
 
 #ifdef __cplusplus
index aaed5ece4a4014f92dd9d8f236c9de242e2160da..a351cab943d0651f77ca86300566d10da3d6d2d0 100644 (file)
@@ -398,6 +398,21 @@ extern "C" {
   }
 #endif // _di_controller_rule_action_read_
 
+#ifndef _di_controller_rule_copy_
+  f_status_t controller_rule_copy(controller_rule_t *source, controller_rule_t *destination) {
+    f_status_t status = F_none;
+
+    f_thread_condition_wait(&source->wait, &destination->lock);
+
+    // @todo
+
+    f_thread_condition_signal(&source->wait);
+    f_thread_mutex_unlock(&destination->lock);
+
+    return status;
+  }
+#endif // _di_controller_rule_copy_
+
 #ifndef _di_controller_rule_error_print_
   void controller_rule_error_print(const fll_error_print_t output, const controller_cache_action_t cache, const bool item) {
 
@@ -437,11 +452,11 @@ extern "C" {
   void controller_rule_error_print_locked(const fll_error_print_t output, const controller_cache_action_t cache, const bool item, controller_thread_t *thread) {
 
     if (output.verbosity != f_console_verbosity_quiet) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread->mutex.print);
 
       controller_rule_error_print(output, cache, item);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread->mutex.print);
     }
   }
 #endif // _di_controller_rule_error_print_
@@ -510,7 +525,7 @@ extern "C" {
 #endif // _di_controller_rule_error_print_need_want_wish_
 
 #ifndef _di_controller_rule_execute_
-  f_status_t controller_rule_execute(const f_array_length_t index, const uint8_t type, const uint8_t options, controller_thread_t *thread, controller_asynchronous_t *asynchronous) {
+  f_status_t controller_rule_execute(const uint8_t type, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) {
     f_status_t status = F_none;
     f_status_t success = F_false;
 
@@ -535,94 +550,95 @@ extern "C" {
     uid_t local_id_user;
     gid_t local_id_group;
 
+    // @todo now that "rule' is a (local) copy, this workaround might no longer be necessary.
     f_int32s_t local_affinity;
     f_control_group_t local_control_group;
     f_int32s_t local_id_groups;
     f_limit_sets_t local_limits;
     f_execute_scheduler_t local_scheduler;
 
-    if (thread->setting->rules.array[index].affinity.used) {
-      local_affinity = thread->setting->rules.array[index].affinity;
+    if (rule->affinity.used) {
+      local_affinity = rule->affinity;
       execute_set.as.affinity = &local_affinity;
     }
 
-    if (thread->setting->rules.array[index].capability) {
-      execute_set.as.capability = thread->setting->rules.array[index].capability;
+    if (rule->capability) {
+      execute_set.as.capability = rule->capability;
     }
 
-    if (thread->setting->rules.array[index].has & controller_rule_has_control_group) {
-      local_control_group = thread->setting->rules.array[index].control_group;
+    if (rule->has & controller_rule_has_control_group) {
+      local_control_group = rule->control_group;
       execute_set.as.control_group = &local_control_group;
 
       // make sure all required cgroup directories exist.
-      if (thread->setting->rules.array[index].status == F_known_not) {
-        status = fll_control_group_prepare(thread->setting->rules.array[index].control_group);
+      if (rule->status == F_known_not) {
+        status = fll_control_group_prepare(rule->control_group);
 
         if (F_status_is_error(status)) {
-          controller_error_print_locked(thread->data->error, F_status_set_fine(status), "fll_control_group_prepare", F_true, thread);
+          controller_error_print_locked(thread_data.data->error, F_status_set_fine(status), "fll_control_group_prepare", F_true, thread_data.thread);
 
-          thread->setting->rules.array[index].status = F_status_set_error(F_failure);
+          rule->status = F_status_set_error(F_failure);
           return status;
         }
       }
     }
 
-    if (thread->setting->rules.array[index].has & controller_rule_has_group) {
-      local_id_group = thread->setting->rules.array[index].group;
+    if (rule->has & controller_rule_has_group) {
+      local_id_group = rule->group;
       execute_set.as.id_group = &local_id_group;
 
-      if (thread->setting->rules.array[index].groups.used) {
-        local_id_groups = thread->setting->rules.array[index].groups;
+      if (rule->groups.used) {
+        local_id_groups = rule->groups;
         execute_set.as.id_groups = &local_id_groups;
       }
     }
 
-    if (thread->setting->rules.array[index].limits.used) {
-      local_limits = thread->setting->rules.array[index].limits;
+    if (rule->limits.used) {
+      local_limits = rule->limits;
       execute_set.as.limits = &local_limits;
     }
 
-    if (thread->setting->rules.array[index].has & controller_rule_has_scheduler) {
-      local_scheduler = thread->setting->rules.array[index].scheduler;
+    if (rule->has & controller_rule_has_scheduler) {
+      local_scheduler = rule->scheduler;
       execute_set.as.scheduler = &local_scheduler;
     }
 
-    if (thread->setting->rules.array[index].has & controller_rule_has_nice) {
-      local_nice = thread->setting->rules.array[index].nice;
+    if (rule->has & controller_rule_has_nice) {
+      local_nice = rule->nice;
       execute_set.as.nice = &local_nice;
     }
 
-    if (thread->setting->rules.array[index].has & controller_rule_has_user) {
-      local_id_user = thread->setting->rules.array[index].user;
+    if (rule->has & controller_rule_has_user) {
+      local_id_user = rule->user;
       execute_set.as.id_user = &local_id_user;
     }
 
-    status = fl_environment_load_names(thread->setting->rules.array[index].environment, &environment);
+    status = fl_environment_load_names(rule->environment, &environment);
 
     if (F_status_is_error(status)) {
-      controller_error_print_locked(thread->data->error, F_status_set_fine(status), "fl_environment_load_names", F_true, thread);
+      controller_error_print_locked(thread_data.data->error, F_status_set_fine(status), "fl_environment_load_names", F_true, thread_data.thread);
 
-      thread->setting->rules.array[index].status = F_status_set_error(F_failure);
+      rule->status = F_status_set_error(F_failure);
       return status;
     }
 
-    for (i = 0; i < thread->setting->rules.array[index].items.used; ++i) {
+    for (i = 0; i < rule->items.used; ++i) {
 
-      if (thread->setting->signal) {
+      if (thread_data.setting->signal) {
         status = F_signal;
         break;
       }
 
-      if (thread->setting->rules.array[index].items.array[i].type == controller_rule_item_type_setting) continue;
+      if (rule->items.array[i].type == controller_rule_item_type_setting) continue;
 
-      for (j = 0; j < thread->setting->rules.array[index].items.array[i].actions.used; ++j) {
+      for (j = 0; j < rule->items.array[i].actions.used; ++j) {
 
-        if (thread->setting->signal) {
+        if (thread_data.setting->signal) {
           status = F_signal;
           break;
         }
 
-        if (thread->setting->rules.array[index].items.array[i].actions.array[j].type != type) continue;
+        if (rule->items.array[i].actions.array[j].type != type) continue;
 
         execute_set.parameter.data = 0;
         execute_set.parameter.option = fl_execute_parameter_option_threadsafe;
@@ -631,55 +647,55 @@ extern "C" {
           execute_set.parameter.option |= fl_execute_parameter_option_return;
         }
 
-        if (thread->setting->rules.array[index].items.array[i].type == controller_rule_item_type_command) {
+        if (rule->items.array[i].type == controller_rule_item_type_command) {
 
-          if (strchr(thread->setting->rules.array[index].items.array[i].actions.array[j].parameters.array[0].string, f_path_separator_s[0])) {
+          if (strchr(rule->items.array[i].actions.array[j].parameters.array[0].string, f_path_separator_s[0])) {
             execute_set.parameter.option |= fl_execute_parameter_option_path;
           }
 
-          status = controller_rule_execute_foreground(index, thread->setting->rules.array[index].items.array[i].type, thread->setting->rules.array[index].items.array[i].actions.array[j], 0, thread->setting->rules.array[index].items.array[i].actions.array[j].parameters, options, &execute_set, thread, asynchronous);
+          status = controller_rule_execute_foreground(rule->items.array[i].type, rule->items.array[i].actions.array[j], 0, rule->items.array[i].actions.array[j].parameters, options, &execute_set, thread_data, rule);
 
           if (status == F_child) break;
 
           if (F_status_is_error(status)) {
-            thread->setting->rules.array[index].items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+            rule->items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
             if (!(options & controller_rule_option_simulate)) break;
           }
 
           success = F_true;
         }
-        else if (thread->setting->rules.array[index].items.array[i].type == controller_rule_item_type_script) {
-          execute_set.parameter.data = &thread->setting->rules.array[index].items.array[i].actions.array[j].parameters.array[0];
+        else if (rule->items.array[i].type == controller_rule_item_type_script) {
+          execute_set.parameter.data = &rule->items.array[i].actions.array[j].parameters.array[0];
 
-          if (thread->setting->rules.array[index].script.used && strchr(thread->setting->rules.array[index].script.string, f_path_separator_s[0])) {
+          if (rule->script.used && strchr(rule->script.string, f_path_separator_s[0])) {
             execute_set.parameter.option |= fl_execute_parameter_option_path;
           }
 
-          status = controller_rule_execute_foreground(index, thread->setting->rules.array[index].items.array[i].type, thread->setting->rules.array[index].items.array[i].actions.array[j], thread->setting->rules.array[index].script.used ? thread->setting->rules.array[index].script.string : controller_default_program_script, arguments_none, options, &execute_set, thread, asynchronous);
+          status = controller_rule_execute_foreground(rule->items.array[i].type, rule->items.array[i].actions.array[j], rule->script.used ? rule->script.string : controller_default_program_script, arguments_none, options, &execute_set, thread_data, rule);
 
           if (status == F_child) break;
 
           if (F_status_is_error(status)) {
-            thread->setting->rules.array[index].items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+            rule->items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
             if (!(options & controller_rule_option_simulate)) break;
           }
 
           success = F_true;
         }
-        else if (thread->setting->rules.array[index].items.array[i].type == controller_rule_item_type_service) {
+        else if (rule->items.array[i].type == controller_rule_item_type_service) {
 
-          if (strchr(thread->setting->rules.array[index].items.array[i].actions.array[j].parameters.array[0].string, f_path_separator_s[0])) {
+          if (strchr(rule->items.array[i].actions.array[j].parameters.array[0].string, f_path_separator_s[0])) {
             execute_set.parameter.option |= fl_execute_parameter_option_path;
           }
 
-          status = controller_rule_execute_pid_with(index, thread->setting->rules.array[index].items.array[i].type, thread->setting->rules.array[index].items.array[i].actions.array[j], 0, thread->setting->rules.array[index].items.array[i].actions.array[j].parameters, options, &execute_set, thread, asynchronous);
+          status = controller_rule_execute_pid_with(rule->items.array[i].type, rule->items.array[i].actions.array[j], 0, rule->items.array[i].actions.array[j].parameters, options, &execute_set, thread_data, rule);
 
           if (status == F_child) break;
 
           if (F_status_is_error(status)) {
-            thread->setting->rules.array[index].items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+            rule->items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
             if (!(options & controller_rule_option_simulate)) break;
           }
@@ -688,18 +704,18 @@ extern "C" {
         }
         else {
 
-          if (thread->data->warning.verbosity == f_console_verbosity_debug) {
-            f_thread_mutex_lock(&thread->mutex->print);
+          if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+            f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-            fprintf(thread->data->warning.to.stream, "%c", f_string_eol_s[0]);
-            fprintf(thread->data->warning.to.stream, "%s%sAction type is unknown, ignoring.%s%c", thread->data->warning.context.before->string, thread->data->warning.prefix ? thread->data->warning.prefix : f_string_empty_s, thread->data->warning.context.after->string, f_string_eol_s[0]);
+            fprintf(thread_data.data->warning.to.stream, "%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->warning.to.stream, "%s%sAction type is unknown, ignoring.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.prefix ? thread_data.data->warning.prefix : f_string_empty_s, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
 
-            controller_rule_error_print(thread->data->warning, *thread->cache_action, F_true);
+            controller_rule_error_print(thread_data.data->warning, cache->action, F_true);
 
-            f_thread_mutex_unlock(&thread->mutex->print);
+            f_thread_mutex_unlock(&thread_data.thread->mutex.print);
           }
 
-          thread->setting->rules.array[index].items.array[i].actions.array[j].status = F_ignore;
+          rule->items.array[i].actions.array[j].status = F_ignore;
 
           if (success != F_true) {
             success = F_ignore;
@@ -721,21 +737,21 @@ extern "C" {
     }
 
     if (F_status_is_error(status) || success == F_false) {
-      thread->setting->rules.array[index].status = F_status_set_error(F_failure);
+      rule->status = F_status_set_error(F_failure);
     }
     else if (success == F_ignore || success == F_busy) {
-      thread->setting->rules.array[index].status = success;
+      rule->status = success;
     }
     else {
-      thread->setting->rules.array[index].status = F_none;
+      rule->status = F_none;
     }
 
-    return thread->setting->rules.array[index].status;
+    return rule->status;
   }
 #endif // _di_controller_rule_execute_
 
 #ifndef _di_controller_rule_execute_foreground_
-  f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread, controller_asynchronous_t *asynchronous) {
+  f_status_t controller_rule_execute_foreground(const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_data_t thread_data, controller_rule_t *rule) {
 
     f_status_t status = F_none;
     int result = 0;
@@ -743,23 +759,23 @@ extern "C" {
 
     if (options & controller_rule_option_simulate) {
 
-      if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-        f_thread_mutex_lock(&thread->mutex->print);
+      if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-        fprintf(thread->data->output.stream, "%c", f_string_eol_s[0]);
-        fprintf(thread->data->output.stream, "Simulating execution of '");
-        fprintf(thread->data->output.stream, "%s%s%s", thread->data->context.title.string, program ? program : arguments.used && arguments.array[0].used ? arguments.array[0].string : f_string_empty_s, thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "' with the arguments: '%s", thread->data->context.important.string);
+        fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+        fprintf(thread_data.data->output.stream, "Simulating execution of '");
+        fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.title.string, program ? program : arguments.used && arguments.array[0].used ? arguments.array[0].string : f_string_empty_s, thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "' with the arguments: '%s", thread_data.data->context.important.string);
 
         for (f_array_length_t i = program ? 0 : 1; i < arguments.used; ++i) {
-          fprintf(thread->data->output.stream, "%s%s", (program && i || !program && i > 1) ? f_string_space_s : "", arguments.array[i].string);
+          fprintf(thread_data.data->output.stream, "%s%s", (program && i || !program && i > 1) ? f_string_space_s : "", arguments.array[i].string);
         } // for
 
-        fprintf(thread->data->output.stream, "%s' from '", thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "%s%s%s", thread->data->context.notable.string, thread->setting->rules.array[index].name.used ? thread->setting->rules.array[index].name.string : f_string_empty_s, thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "%s'.%c", thread->data->context.reset.string, f_string_eol_s[0]);
+        fprintf(thread_data.data->output.stream, "%s' from '", thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.notable.string, rule->name.used ? rule->name.string : f_string_empty_s, thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "%s'.%c", thread_data.data->context.reset.string, f_string_eol_s[0]);
 
-        f_thread_mutex_unlock(&thread->mutex->print);
+        f_thread_mutex_unlock(&thread_data.thread->mutex.print);
       }
 
       // sleep for less than a second to better show simulation of synchronous vs asynchronous.
@@ -779,13 +795,13 @@ extern "C" {
       result = 0;
 
       // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process.
-      asynchronous->child = id_process;
+      thread_data.thread->asynchronouss.array[thread_data.id].child = id_process;
 
       // have the parent wait for the child process to finish.
       waitpid(id_process, &result, WUNTRACED | WCONTINUED);
 
       // remove the pid now that waidpid() has returned.
-      asynchronous->child = 0;
+      thread_data.thread->asynchronouss.array[thread_data.id].child = 0;
 
       // this must explicitly check for 0 (as opposed to checking (!result)).
       if (!WIFEXITED(result)) {
@@ -815,19 +831,19 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
       if (status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
-        controller_rule_error_print_execute(thread->data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
+        controller_rule_error_print_execute(thread_data.data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
       }
       else if (status == F_file_found_not) {
-        controller_rule_error_print_execute_not_found(thread->data->error, F_false, program);
+        controller_rule_error_print_execute_not_found(thread_data.data->error, F_false, program);
       }
       else {
-        fll_error_print(thread->data->error, status, "fll_execute_program", F_true);
+        fll_error_print(thread_data.data->error, status, "fll_execute_program", F_true);
       }
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       status = F_status_set_error(status);
     }
@@ -837,7 +853,7 @@ extern "C" {
 #endif // _di_controller_rule_execute_foreground_
 
 #ifndef _di_controller_rule_execute_pid_with_
-  f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread, controller_asynchronous_t *asynchronous) {
+  f_status_t controller_rule_execute_pid_with(const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_data_t thread_data, controller_rule_t *rule) {
 
     f_status_t status = F_none;
     int result = 0;
@@ -852,23 +868,23 @@ extern "C" {
 
     if (options & controller_rule_option_simulate) {
 
-      if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-        f_thread_mutex_lock(&thread->mutex->print);
+      if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-        fprintf(thread->data->output.stream, "%c", f_string_eol_s[0]);
-        fprintf(thread->data->output.stream, "Simulating execution of '");
-        fprintf(thread->data->output.stream, "%s%s%s", thread->data->context.title.string, program ? program : arguments.used && arguments.array[0].used ? arguments.array[0].string : f_string_empty_s, thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "' with the arguments: '%s", thread->data->context.important.string);
+        fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+        fprintf(thread_data.data->output.stream, "Simulating execution of '");
+        fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.title.string, program ? program : arguments.used && arguments.array[0].used ? arguments.array[0].string : f_string_empty_s, thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "' with the arguments: '%s", thread_data.data->context.important.string);
 
         for (f_array_length_t i = program ? 0 : 1; i < arguments.used; ++i) {
-          fprintf(thread->data->output.stream, "%s%s", (program && i || !program && i > 1) ? f_string_space_s : "", arguments.array[i].string);
+          fprintf(thread_data.data->output.stream, "%s%s", (program && i || !program && i > 1) ? f_string_space_s : "", arguments.array[i].string);
         } // for
 
-        fprintf(thread->data->output.stream, "%s' from '", thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "%s%s%s", thread->data->context.notable.string, thread->setting->rules.array[index].name.used ? thread->setting->rules.array[index].name.string : f_string_empty_s, thread->data->context.reset.string);
-        fprintf(thread->data->output.stream, "%s'.%c", thread->data->context.reset.string, f_string_eol_s[0]);
+        fprintf(thread_data.data->output.stream, "%s' from '", thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.notable.string, rule->name.used ? rule->name.string : f_string_empty_s, thread_data.data->context.reset.string);
+        fprintf(thread_data.data->output.stream, "%s'.%c", thread_data.data->context.reset.string, f_string_eol_s[0]);
 
-        f_thread_mutex_unlock(&thread->mutex->print);
+        f_thread_mutex_unlock(&thread_data.thread->mutex.print);
       }
 
       // sleep for less than a second to better show simulation of synchronous vs asynchronous.
@@ -888,13 +904,13 @@ extern "C" {
       result = 0;
 
       // assign the child process id to the asynchronous thread to allow for the cancel process to send appropriate termination signals to the child process.
-      asynchronous->child = id_process;
+      thread_data.thread->asynchronouss.array[thread_data.id].child = id_process;
 
       // have the parent wait for the child process to finish.
       waitpid(id_process, &result, WUNTRACED | WCONTINUED);
 
       // remove the pid now that waidpid() has returned.
-      asynchronous->child = 0;
+      thread_data.thread->asynchronouss.array[thread_data.id].child = 0;
 
       // this must explicitly check for 0 (as opposed to checking (!result)).
       if (!WIFEXITED(result)) {
@@ -924,19 +940,19 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
       if (status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
-        controller_rule_error_print_execute(thread->data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
+        controller_rule_error_print_execute(thread_data.data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
       }
       else if (status == F_file_found_not) {
-        controller_rule_error_print_execute_not_found(thread->data->error, F_false, program);
+        controller_rule_error_print_execute_not_found(thread_data.data->error, F_false, program);
       }
       else {
-        fll_error_print(thread->data->error, status, "fll_execute_program", F_true);
+        fll_error_print(thread_data.data->error, status, "fll_execute_program", F_true);
       }
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return F_status_set_error(status);
     }
@@ -948,15 +964,15 @@ extern "C" {
 #endif // _di_controller_rule_execute_pid_with_
 
 #ifndef _di_controller_rule_find_loaded_
-  f_array_length_t controller_rule_find_loaded(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id) {
+  f_array_length_t controller_rule_find_loaded(const f_string_static_t rule_id, controller_thread_data_t thread_data) {
 
-    f_array_length_t i = 0;
+    f_array_length_t i = thread_data.setting->rules.used;
 
-    for (; i < setting.rules.used; ++i) {
-      if (fl_string_dynamic_compare(rule_id, setting.rules.array[i].id) == F_equal_to) break;
+    for (; i; --i) {
+      if (fl_string_dynamic_compare(rule_id, thread_data.setting->rules.array[i].id) == F_equal_to) return i;
     } // for
 
-    return i;
+    return thread_data.setting->rules.used;
   }
 #endif // _di_controller_rule_find_loaded_
 
@@ -1002,7 +1018,7 @@ extern "C" {
     f_status_t status = F_none;
 
     f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_item.used);
-    f_string_length_t last = 0;
+    f_array_length_t last = 0;
 
     uint8_t type = 0;
     uint8_t method = 0;
@@ -1365,7 +1381,7 @@ extern "C" {
 #endif // _di_controller_rule_path_
 
 #ifndef _di_controller_rule_process_
-  f_status_t controller_rule_process(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_t *thread, controller_asynchronous_t *asynchronous) {
+  f_status_t controller_rule_process(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) {
 
     switch (action) {
       case controller_rule_action_type_freeze:
@@ -1381,64 +1397,64 @@ extern "C" {
 
       default:
 
-        if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-          f_thread_mutex_lock(&thread->mutex->print);
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-          fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(thread->data->error.to.stream, "%s%sUnsupported action type '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s);
-          fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, controller_rule_action_type_name(action), thread->data->error.notable.after->string);
-          fprintf(thread->data->error.to.stream, "%s' while attempting to execute rule.%s%c", thread->data->error.context.before->string, thread->data->error.context.after->string, f_string_eol_s[0]);
+          fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+          fprintf(thread_data.data->error.to.stream, "%s%sUnsupported action type '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_rule_action_type_name(action), thread_data.data->error.notable.after->string);
+          fprintf(thread_data.data->error.to.stream, "%s' while attempting to execute rule.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
 
-          controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+          controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-          f_thread_mutex_unlock(&thread->mutex->print);
+          f_thread_mutex_unlock(&thread_data.thread->mutex.print);
         }
 
         return F_status_set_error(F_parameter);
     }
 
-    if (index >= thread->setting->rules.used) {
-      f_thread_mutex_lock(&thread->mutex->print);
+    if (index >= thread_data.setting->rules.used) {
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_parameter, "controller_rule_process", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_parameter, "controller_rule_process", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return F_status_set_error(F_parameter);
     }
 
     f_status_t status = F_none;
 
-    f_macro_array_lengths_t_increase_by(status, (*thread->stack), controller_default_allocation_step)
+    f_macro_array_lengths_t_increase_by(status, thread_data.thread->asynchronouss.array[thread_data.id].stack, controller_default_allocation_step)
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return status;
     }
 
     f_array_length_t i = 0;
 
-    for (; i < thread->stack->used; ++i) {
+    for (; i < thread_data.thread->asynchronouss.array[thread_data.id].stack.used; ++i) {
 
-      if (thread->stack->array[i] == index) {
-        if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-          f_thread_mutex_lock(&thread->mutex->print);
+      if (thread_data.thread->asynchronouss.array[thread_data.id].stack.array[i] == index) {
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-          fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(thread->data->error.to.stream, "%s%sThe rule '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s);
-          fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, thread->setting->rules.array[i].name.string, thread->data->error.notable.after->string);
-          fprintf(thread->data->error.to.stream, "%s' is already on the execution stack, this recursion is prohibited.%s%c", thread->data->error.context.before->string, thread->data->error.context.after->string, f_string_eol_s[0]);
+          fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+          fprintf(thread_data.data->error.to.stream, "%s%sThe rule '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, thread_data.setting->rules.array[i].name.string, thread_data.data->error.notable.after->string);
+          fprintf(thread_data.data->error.to.stream, "%s' is already on the execution stack, this recursion is prohibited.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
 
-          controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+          controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-          f_thread_mutex_unlock(&thread->mutex->print);
+          f_thread_mutex_unlock(&thread_data.thread->mutex.print);
         }
 
         // never continue on recursion errors even in simulate mode.
@@ -1446,76 +1462,89 @@ extern "C" {
       }
     }
 
-    thread->cache_action->name_action.used = 0;
-    thread->cache_action->name_item.used = 0;
-    thread->cache_action->name_file.used = 0;
+    cache->action.name_action.used = 0;
+    cache->action.name_item.used = 0;
+    cache->action.name_file.used = 0;
 
-    status = f_string_append(controller_string_rules_s, controller_string_rules_length, &thread->cache_action->name_file);
+    status = f_string_append(controller_string_rules_s, controller_string_rules_length, &cache->action.name_file);
 
     if (F_status_is_error_not(status)) {
-      status = f_string_append(f_path_separator_s, f_path_separator_length, &thread->cache_action->name_file);
+      status = f_string_append(f_path_separator_s, f_path_separator_length, &cache->action.name_file);
     }
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_status_set_fine(status), "f_string_append", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_append", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return status;
     }
 
-    status = f_string_dynamic_append(thread->setting->rules.array[index].id, &thread->cache_action->name_file);
+    status = f_string_dynamic_append(thread_data.setting->rules.array[index].id, &cache->action.name_file);
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return status;
     }
 
-    status = f_string_append(f_path_extension_separator, f_path_extension_separator_length, &thread->cache_action->name_file);
+    status = f_string_append(f_path_extension_separator, f_path_extension_separator_length, &cache->action.name_file);
 
     if (F_status_is_error_not(status)) {
-      status = f_string_append(controller_string_rule_s, controller_string_rule_length, &thread->cache_action->name_file);
+      status = f_string_append(controller_string_rule_s, controller_string_rule_length, &cache->action.name_file);
     }
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_status_set_fine(status), "f_string_append", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_append", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return status;
     }
 
-    status = f_string_dynamic_terminate_after(&thread->cache_action->name_file);
+    status = f_string_dynamic_terminate_after(&cache->action.name_file);
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread->mutex->print);
+      f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-      fll_error_print(thread->data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
-      controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+      controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-      f_thread_mutex_unlock(&thread->mutex->print);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
       return status;
     }
 
-    thread->stack->array[thread->stack->used++] = index;
+    thread_data.thread->asynchronouss.array[thread_data.id].stack.array[thread_data.thread->asynchronouss.array[thread_data.id].stack.used++] = index;
 
-    controller_rule_t *rule = &thread->setting->rules.array[index];
+    controller_rule_t *rule = &thread_data.setting->rules.array[index];
 
-    if ((options & controller_rule_option_simulate) && thread->data->parameters[controller_parameter_validate].result == f_console_result_found) {
-      controller_rule_simulate(index, controller_rule_action_type_start, options, thread);
+    if ((options & controller_rule_option_simulate) && thread_data.data->parameters[controller_parameter_validate].result == f_console_result_found) {
+      // @fixme the cache should probably store a rule type and then that can used instead of calling controller_rule_copy() here.
+      controller_rule_t rule_simulate = controller_rule_t_initialize;
+
+      status = controller_rule_copy(rule, &rule_simulate);
+
+      if (F_status_is_error(status)) {
+        controller_rule_delete_simple(&rule_simulate);
+
+        return status;
+      }
+
+      controller_rule_simulate(controller_rule_action_type_start, options, thread_data, cache, &rule_simulate);
+
+      controller_rule_delete_simple(&rule_simulate);
     }
 
     {
@@ -1538,165 +1567,167 @@ extern "C" {
       for (i = 0; i < 3; ++i) {
 
         for (j = 0; j < dynamics[i]->used; ++j) {
-          at = controller_rule_find_loaded(*thread->data, *thread->setting, dynamics[i]->array[j]);
+          at = controller_rule_find_loaded(*thread_data.data, *thread_data.setting, dynamics[i]->array[j]);
 
-          if (at == thread->setting->rules.used) {
+          if (at == thread_data.setting->rules.used) {
             if (i == 0) {
-              f_thread_mutex_lock(&thread->mutex->print);
+              f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-              controller_rule_error_print_need_want_wish(thread->data->error, strings[i], dynamics[i]->array[j].string, "was not found");
+              controller_rule_error_print_need_want_wish(thread_data.data->error, strings[i], dynamics[i]->array[j].string, "was not found");
 
               status = F_status_set_error(F_found_not);
-              controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+              controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-              f_thread_mutex_unlock(&thread->mutex->print);
+              f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
               if (!(options & controller_rule_option_simulate)) break;
             }
             else {
-              if (thread->data->warning.verbosity == f_console_verbosity_debug) {
-                f_thread_mutex_lock(&thread->mutex->print);
+              if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+                f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                controller_rule_error_print_need_want_wish(thread->data->warning, strings[i], dynamics[i]->array[j].string, "was not found");
-                controller_rule_error_print(thread->data->warning, *thread->cache_action, F_true);
+                controller_rule_error_print_need_want_wish(thread_data.data->warning, strings[i], dynamics[i]->array[j].string, "was not found");
+                controller_rule_error_print(thread_data.data->warning, cache->action, F_true);
 
-                f_thread_mutex_unlock(&thread->mutex->print);
+                f_thread_mutex_unlock(&thread_data.thread->mutex.print);
               }
             }
           }
 
-          if (F_status_is_error_not(status) && at < thread->setting->rules.used) {
+          if (F_status_is_error_not(status) && at < thread_data.setting->rules.used) {
 
-            f_thread_mutex_lock(&thread->setting->rules.array[at].lock);
+            f_thread_mutex_lock(&thread_data.setting->rules.array[at].lock);
 
-            while (thread->asynchronouss.enabled && thread->setting->rules.array[at].status == F_known_not) {
-              f_thread_condition_wait(&thread->setting->rules.array[at].wait, &thread->setting->rules.array[at].lock);
+            while (thread_data.enabled && thread_data.setting->rules.array[at].status == F_known_not) {
+              f_thread_condition_wait(&thread_data.setting->rules.array[at].wait, &thread_data.setting->rules.array[at].lock);
             } // while
 
-            if (!thread->asynchronouss.enabled) {
-              f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-              f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+            if (!thread_data.enabled) {
+              f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+              f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
 
               status = F_signal;
               break;
             }
 
             // when the status is unknown, then the rule has not been executed, so attempt to execute it.
-            if (thread->setting->rules.array[at].status == F_known_not) {
+            if (thread_data.setting->rules.array[at].status == F_known_not) {
 
-              f_macro_array_lengths_t_increase_by(status, (*thread->stack), controller_default_allocation_step)
+              f_macro_array_lengths_t_increase_by(status, thread_data.thread->asynchronouss.array[thread_data.id].stack, controller_default_allocation_step)
 
               if (F_status_is_error(status)) {
-                f_thread_mutex_lock(&thread->mutex->print);
+                f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                fll_error_print(thread->data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
-                controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+                fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true);
+                controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-                f_thread_mutex_unlock(&thread->mutex->print);
+                f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
-                f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-                f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+                f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+                f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
 
                 // always exit on memory errors, even in simulate mode.
                 break;
               }
 
               // rule execution will re-use the existing cache, so save the current cache.
-              const f_array_length_t cache_line_action = thread->cache_action->line_action;
-              const f_array_length_t cache_line_item = thread->cache_action->line_item;
+              const f_array_length_t cache_line_action = cache->action.line_action;
+              const f_array_length_t cache_line_item = cache->action.line_item;
 
-              const f_string_length_t cache_name_action_used = thread->cache_action->name_action.used;
-              const f_string_length_t cache_name_item_used = thread->cache_action->name_item.used;
-              const f_string_length_t cache_name_file_used = thread->cache_action->name_file.used;
+              const f_array_length_t cache_name_action_used = cache->action.name_action.used;
+              const f_array_length_t cache_name_item_used = cache->action.name_item.used;
+              const f_array_length_t cache_name_file_used = cache->action.name_file.used;
 
               char cache_name_action[cache_name_action_used];
               char cache_name_item[cache_name_item_used];
               char cache_name_file[cache_name_file_used];
 
-              memcpy(cache_name_action, thread->cache_action->name_action.string, thread->cache_action->name_action.used);
-              memcpy(cache_name_item, thread->cache_action->name_item.string, thread->cache_action->name_item.used);
-              memcpy(cache_name_file, thread->cache_action->name_file.string, thread->cache_action->name_file.used);
+              memcpy(cache_name_action, cache->action.name_action.string, cache->action.name_action.used);
+              memcpy(cache_name_item, cache->action.name_item.string, cache->action.name_item.used);
+              memcpy(cache_name_file, cache->action.name_file.string, cache->action.name_file.used);
+
+              // @fixme the cache should probably store a rule type and then that can used instead of calling controller_rule_copy(), which would need to be copied here just like the other cache properties.
 
               // recursive rule processing is to always be synchronous.
               status = controller_rule_process(at, action, options & controller_rule_option_asynchronous ? options - controller_rule_option_asynchronous : options, thread, 0);
 
               if (status == F_child || status == F_signal) {
-                f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-                f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+                f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+                f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
 
                 break;
               }
 
               // restore cache.
-              memcpy(thread->cache_action->name_action.string, cache_name_action, cache_name_action_used);
-              memcpy(thread->cache_action->name_item.string, cache_name_item, cache_name_item_used);
-              memcpy(thread->cache_action->name_file.string, cache_name_file, cache_name_file_used);
+              memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
+              memcpy(cache->action.name_item.string, cache_name_item, cache_name_item_used);
+              memcpy(cache->action.name_file.string, cache_name_file, cache_name_file_used);
 
-              thread->cache_action->name_action.string[cache_name_action_used] = 0;
-              thread->cache_action->name_item.string[cache_name_item_used] = 0;
+              cache->action.name_action.string[cache_name_action_used] = 0;
+              cache->action.name_item.string[cache_name_item_used] = 0;
 
-              thread->cache_action->name_action.used = cache_name_action_used;
-              thread->cache_action->name_item.used = cache_name_item_used;
-              thread->cache_action->name_file.used = cache_name_file_used;
+              cache->action.name_action.used = cache_name_action_used;
+              cache->action.name_item.used = cache_name_item_used;
+              cache->action.name_file.used = cache_name_file_used;
 
-              thread->cache_action->line_action = cache_line_action;
-              thread->cache_action->line_item = cache_line_item;
+              cache->action.line_action = cache_line_action;
+              cache->action.line_item = cache_line_item;
 
               if (F_status_is_error(status)) {
                 if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not) {
 
-                  f_thread_mutex_lock(&thread->mutex->print);
+                  f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                  controller_rule_error_print_need_want_wish(thread->data->error, strings[i], dynamics[i]->array[j].string, "failed during execution");
-                  controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+                  controller_rule_error_print_need_want_wish(thread_data.data->error, strings[i], dynamics[i]->array[j].string, "failed during execution");
+                  controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-                  f_thread_mutex_unlock(&thread->mutex->print);
+                  f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
                   if (!(options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) {
-                    f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-                    f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+                    f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+                    f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
 
                     break;
                   }
                 }
                 else {
-                  if (thread->data->warning.verbosity == f_console_verbosity_debug) {
-                    f_thread_mutex_lock(&thread->mutex->print);
+                  if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+                    f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                    controller_rule_error_print_need_want_wish(thread->data->warning, strings[i], dynamics[i]->array[j].string, "failed during execution");
-                    controller_rule_error_print(thread->data->warning, *thread->cache_action, F_true);
+                    controller_rule_error_print_need_want_wish(thread_data.data->warning, strings[i], dynamics[i]->array[j].string, "failed during execution");
+                    controller_rule_error_print(thread_data.data->warning, cache->action, F_true);
 
-                    f_thread_mutex_unlock(&thread->mutex->print);
+                    f_thread_mutex_unlock(&thread_data.thread->mutex.print);
                   }
                 }
               }
             }
 
-            f_thread_condition_signal(&thread->setting->rules.array[at].wait);
-            f_thread_mutex_unlock(&thread->setting->rules.array[at].lock);
+            f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
+            f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
 
-            if (F_status_is_error(thread->setting->rules.array[at].status)) {
+            if (F_status_is_error(thread_data.setting->rules.array[at].status)) {
 
               if (i == 0 || i == 1) {
-                f_thread_mutex_lock(&thread->mutex->print);
+                f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                controller_rule_error_print_need_want_wish(thread->data->error, strings[i], dynamics[i]->array[j].string, "is in a failed state");
+                controller_rule_error_print_need_want_wish(thread_data.data->error, strings[i], dynamics[i]->array[j].string, "is in a failed state");
 
                 status = F_status_set_error(F_found_not);
-                controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+                controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-                f_thread_mutex_unlock(&thread->mutex->print);
+                f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
                 if (!(options & controller_rule_option_simulate)) break;
               }
               else {
-                if (thread->data->warning.verbosity == f_console_verbosity_debug) {
-                  f_thread_mutex_lock(&thread->mutex->print);
+                if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+                  f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-                  controller_rule_error_print_need_want_wish(thread->data->warning, strings[i], dynamics[i]->array[j].string, "is in a failed state");
-                  controller_rule_error_print(thread->data->warning, *thread->cache_action, F_true);
+                  controller_rule_error_print_need_want_wish(thread_data.data->warning, strings[i], dynamics[i]->array[j].string, "is in a failed state");
+                  controller_rule_error_print(thread_data.data->warning, cache->action, F_true);
 
-                  f_thread_mutex_unlock(&thread->mutex->print);
+                  f_thread_mutex_unlock(&thread_data.thread->mutex.print);
                 }
               }
             }
@@ -1713,7 +1744,7 @@ extern "C" {
       return status;
     }
 
-    if (!thread->asynchronouss.enabled) {
+    if (!thread_data.enabled) {
       return F_signal;
     }
 
@@ -1721,7 +1752,7 @@ extern "C" {
     if (!(options & controller_rule_option_wait) && F_status_is_error_not(status)) {
       controller_rule_wait_all(thread);
 
-      if (!thread->asynchronouss.enabled) {
+      if (!thread_data.enabled) {
         return F_signal;
       }
     }
@@ -1747,19 +1778,19 @@ extern "C" {
 
         if (missing) {
 
-          if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-            f_thread_mutex_lock(&thread->mutex->print);
+          if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+            f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
-            fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]);
-            fprintf(thread->data->error.to.stream, "%s%sThe rule '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s);
-            fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, rule->name.used ? rule->name.string : f_string_empty_s, thread->data->error.notable.after->string);
-            fprintf(thread->data->error.to.stream, "%s' has no '", thread->data->error.context.before->string);
-            fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, controller_rule_action_type_name(action).string, thread->data->error.notable.after->string);
-            fprintf(thread->data->error.to.stream, "%s' action to execute.%s%c", thread->data->error.context.before->string, thread->data->error.context.after->string, f_string_eol_s[0]);
+            fprintf(thread_data.data->error.to.stream, "%c", f_string_eol_s[0]);
+            fprintf(thread_data.data->error.to.stream, "%s%sThe rule '", 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%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, rule->name.used ? rule->name.string : f_string_empty_s, thread_data.data->error.notable.after->string);
+            fprintf(thread_data.data->error.to.stream, "%s' has no '", thread_data.data->error.context.before->string);
+            fprintf(thread_data.data->error.to.stream, "%s%s%s%s", thread_data.data->error.context.after->string, thread_data.data->error.notable.before->string, controller_rule_action_type_name(action).string, thread_data.data->error.notable.after->string);
+            fprintf(thread_data.data->error.to.stream, "%s' action to execute.%s%c", thread_data.data->error.context.before->string, thread_data.data->error.context.after->string, f_string_eol_s[0]);
 
-            controller_rule_error_print(thread->data->error, *thread->cache_action, F_true);
+            controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-            f_thread_mutex_unlock(&thread->mutex->print);
+            f_thread_mutex_unlock(&thread_data.thread->mutex.print);
           }
 
           status = F_status_set_error(F_parameter);
@@ -1767,24 +1798,36 @@ extern "C" {
       }
 
       if (F_status_is_error_not(status)) {
-        status = controller_rule_execute(index, action, options, thread, asynchronous);
+        // @fixme the cache should probably store a rule type and then that can used instead of calling controller_rule_copy() here.
+        controller_rule_t rule = controller_rule_t_initialize;
+
+        status = controller_rule_copy(&thread_data.setting->rules.array[index], &rule);
+
+        if (F_status_is_error_not(status)) {
+          status = controller_rule_execute(action, options, thread_data, &cache, &rule);
+
+          // @fixme there needs to be a new rule structure for storing the status state and the rule data, for any given execution (this should also be locking and unlocking).
+          thread_data.setting->rules.array[index].status = rule.status;
+        }
+
+        controller_rule_delete_simple(&rule);
 
         if (status == F_child) {
           return F_child;
         }
 
-        if (!thread->asynchronouss.enabled) {
+        if (!thread_data.enabled) {
           return F_signal;
         }
 
         if (F_status_is_error(status)) {
-          controller_rule_error_print_locked(thread->data->error, *thread->cache_action, F_true, thread);
+          controller_rule_error_print_locked(thread_data.data->error, cache->action, F_true, thread);
         }
       }
     }
 
     // remove this rule off the stack.
-    thread->stack->used--;
+    thread_data.thread->asynchronouss.array[thread_data.id].stack.used--;
 
     if (F_status_is_error(status)) {
       return status;
@@ -1795,25 +1838,25 @@ extern "C" {
 #endif // _di_controller_rule_process_
 
 #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_t *thread) {
+  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_thread_mutex_lock(&thread->mutex->asynchronous);
+    f_thread_mutex_lock(&thread_data->mutex.asynchronous);
 
-    if (!thread->asynchronouss.enabled) {
-      f_thread_mutex_unlock(&thread->mutex->asynchronous);
+    if (!thread_data.thread->enabled) {
+      f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
 
       return F_signal;
     }
 
-    f_status_t status = controller_asynchronouss_increase(&thread->asynchronouss);
+    f_status_t status = controller_asynchronouss_increase(&thread_data.thread->asynchronouss);
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_unlock(&thread->mutex->asynchronous);
+      f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
 
       return status;
     }
 
-    controller_asynchronous_t *asynchronous = &thread->asynchronouss.array[thread->asynchronouss.used++];
+    controller_asynchronous_t *asynchronous = &thread_data.thread->asynchronouss.array[thread_data.thread->asynchronouss.used++];
 
     controller_macro_asynchronous_t_clear((*asynchronous));
 
@@ -1821,18 +1864,18 @@ extern "C" {
     asynchronous->state = controller_asynchronous_state_active;
     asynchronous->action = action;
     asynchronous->options = options;
-    asynchronous->thread = (void *) thread;
-    asynchronous->cache.line_action = thread->cache_action->line_action;
-    asynchronous->cache.line_item = thread->cache_action->line_item;
+    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;
 
-    status = f_string_dynamic_append(thread->cache_action->name_action, &asynchronous->cache.name_action);
+    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->cache_action->name_file, &asynchronous->cache.name_file);
+      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->cache_action->name_item, &asynchronous->cache.name_item);
+      status = f_string_dynamic_append(thread_data.thread->cache_action->name_item, &asynchronous->cache.name_item);
     }
 
     if (F_status_is_error_not(status)) {
@@ -1842,10 +1885,10 @@ extern "C" {
     if (F_status_is_error(status)) {
       controller_macro_asynchronous_t_delete_simple((*asynchronous));
 
-      thread->asynchronouss.used--;
+      thread_data.thread->asynchronouss.used--;
     }
 
-    f_thread_mutex_unlock(&thread->mutex->asynchronous);
+    f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
 
     if (F_status_is_error(status)) {
       return status;
@@ -1856,7 +1899,7 @@ extern "C" {
 #endif // _di_controller_rule_process_asynchronous_
 
 #ifndef _di_controller_rule_read_
-  f_status_t controller_rule_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id, controller_thread_t *thread, controller_cache_t *cache, controller_rule_t *rule) {
+  f_status_t controller_rule_read(const f_string_static_t rule_id, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) {
     f_status_t status = F_none;
 
     bool for_item = F_true;
@@ -1940,16 +1983,16 @@ extern "C" {
     status = f_string_dynamic_append_nulless(rule_id, &rule->id);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
+      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
     }
     else {
       status = f_string_dynamic_terminate_after(&rule->id);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
       }
       else {
-        status = controller_file_load(data, setting, controller_string_rules_s, rule->id, controller_string_rule_s, controller_string_rules_length, controller_string_rule_length, cache);
+        status = controller_file_load(controller_string_rules_s, rule->id, controller_string_rule_s, controller_string_rules_length, controller_string_rule_length, thread_data, cache);
       }
     }
 
@@ -1962,13 +2005,13 @@ extern "C" {
         status = fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments);
 
         if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
+          fll_error_print(thread_data.data->error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
         }
         else {
           status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
           }
         }
       }
@@ -1978,7 +2021,7 @@ extern "C" {
       status = controller_rule_items_increase_by(cache->object_items.used, &rule->items);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true);
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true);
       }
       else {
         f_array_length_t i = 0;
@@ -2015,7 +2058,7 @@ extern "C" {
           status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->action.line_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
             break;
           }
 
@@ -2024,7 +2067,7 @@ extern "C" {
           status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_items.array[i], &cache->action.name_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
             break;
           }
 
@@ -2041,14 +2084,14 @@ extern "C" {
             rule->items.array[rule->items.used].type = controller_rule_item_type_service;
           }
           else {
-            if (data.warning.verbosity == f_console_verbosity_debug) {
-              fprintf(data.warning.to.stream, "%s%sUnknown rule item '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-              fprintf(data.warning.to.stream, "%s%s", data.warning.context.after->string, data.warning.notable.before->string);
-              f_print_dynamic(data.warning.to.stream, cache->action.name_item);
-              fprintf(data.warning.to.stream, "%s", data.warning.notable.after->string);
-              fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
-
-              controller_rule_error_print(data.warning, cache->action, F_true);
+            if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              fprintf(thread_data.data->warning.to.stream, "%s%sUnknown rule item '", 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.context.after->string, thread_data.data->warning.notable.before->string);
+              f_print_dynamic(thread_data.data->warning.to.stream, cache->action.name_item);
+              fprintf(thread_data.data->warning.to.stream, "%s", thread_data.data->warning.notable.after->string);
+              fprintf(thread_data.data->warning.to.stream, "%s'.%s%c", thread_data.data->warning.context.before->string, thread_data.data->warning.context.after->string, f_string_eol_s[0]);
+
+              controller_rule_error_print(thread_data.data->warning, cache->action, F_true);
             }
 
             continue;
@@ -2057,19 +2100,19 @@ extern "C" {
           status = f_string_dynamic_partial_append(cache->buffer_file, cache->content_items.array[i].array[0], &cache->buffer_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_partial_append", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_partial_append", F_true);
             break;
           }
 
           status = f_string_dynamic_terminate_after(&cache->buffer_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+            fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
             break;
           }
 
           if (rule->items.array[rule->items.used].type) {
-            status = controller_rule_item_read(data, cache, &rule->items.array[rule->items.used]);
+            status = controller_rule_item_read(thread_data, cache, &rule->items.array[rule->items.used]);
             if (F_status_is_error(status)) break;
 
             rule->items.used++;
@@ -2077,7 +2120,7 @@ extern "C" {
           else {
             for_item = F_false;
 
-            status = controller_rule_setting_read(data, setting, cache, rule);
+            status = controller_rule_setting_read(thread_data, cache, rule);
 
             if (F_status_is_error(status)) {
               if (F_status_set_fine(status) == F_memory_not) {
@@ -2090,7 +2133,7 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      controller_rule_error_print(data.error, cache->action, for_item);
+      controller_rule_error_print(thread_data.data->error, cache->action, for_item);
 
       rule->status = controller_status_simplify(F_status_set_fine(status));
       return F_false;
@@ -2117,7 +2160,7 @@ extern "C" {
       return status;
     }
 
-    f_string_length_t path_original_length = 0;
+    f_array_length_t path_original_length = 0;
     f_string_dynamic_t *setting_value = 0;
     f_string_dynamics_t *setting_values = 0;
     f_string_maps_t *setting_maps = 0;
@@ -3752,12 +3795,12 @@ extern "C" {
 #endif // _di_controller_rule_setting_read_
 
 #ifndef _di_controller_rule_simulate_
-  void controller_rule_simulate(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_t *thread) {
+  void controller_rule_simulate(const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) {
 
-    controller_data_t *data = thread->data;
-    controller_setting_t *setting = thread->setting;
+    controller_data_t *data = thread_data.thread->data;
+    controller_setting_t *setting = thread_data.thread->setting;
 
-    f_thread_mutex_lock(&thread->mutex->print);
+    f_thread_mutex_lock(&thread_data.thread->mutex.print);
 
     switch (action) {
       case controller_rule_action_type_kill:
@@ -3775,10 +3818,10 @@ extern "C" {
           fprintf(data->error.to.stream, "%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, controller_rule_action_type_name(action).string, data->error.notable.after->string);
           fprintf(data->error.to.stream, "%s' while attempting to simulate rule execution.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
 
-          controller_rule_error_print(data->error, *thread->cache_action, F_true);
+          controller_rule_error_print(data->error, cache->action, F_true);
         }
 
-        f_thread_mutex_unlock(&thread->mutex->print);
+        f_thread_mutex_unlock(&thread_data.thread->mutex.print);
 
         return;
     }
@@ -3827,10 +3870,10 @@ extern "C" {
       fprintf(data->output.stream, "  %s%s%s ", data->context.set.important.before->string, controller_string_capability_s, data->context.set.important.after->string);
 
       if (rule->capability) {
-        thread->cache_action->generic.used = 0;
+        cache->action.generic.used = 0;
 
-        if (F_status_is_error_not(f_capability_to_text(rule->capability, &thread->cache_action->generic))) {
-          fprintf(data->output.stream, "%s", thread->cache_action->generic.string);
+        if (F_status_is_error_not(f_capability_to_text(rule->capability, &cache->action.generic))) {
+          fprintf(data->output.stream, "%s", cache->action.generic.string);
         }
       }
 
@@ -4054,14 +4097,14 @@ extern "C" {
 
     setting->rules.array[index].status = F_complete;
 
-    f_thread_mutex_unlock(&thread->mutex->print);
+    f_thread_mutex_unlock(&thread_data.thread->mutex.print);
   }
 #endif // _di_controller_rule_simulate_
 
 #ifndef _di_controller_rule_wait_all_
   void controller_rule_wait_all(controller_thread_t *thread) {
 
-    for (f_array_length_t i = 0; i < thread->asynchronouss.used && thread->asynchronouss.enabled; ++i) {
+    for (f_array_length_t i = 0; i < thread->asynchronouss.used && thread->enabled; ++i) {
 
       // do not need to wait when state is 0 or joined.
       if (!thread->asynchronouss.array[i].state || thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) {
@@ -4074,21 +4117,21 @@ extern "C" {
 
       f_thread_mutex_lock(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock);
 
-      while (thread->asynchronouss.enabled && thread->setting->rules.array[thread->asynchronouss.array[i].index].status == F_known_not) {
+      while (thread->enabled && thread->setting->rules.array[thread->asynchronouss.array[i].index].status == F_known_not) {
         f_thread_condition_wait(&thread->setting->rules.array[thread->asynchronouss.array[i].index].wait, &thread->setting->rules.array[thread->asynchronouss.array[i].index].lock);
       } // while
 
       f_thread_mutex_unlock(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock);
 
-      if (!thread->asynchronouss.enabled) break;
+      if (!thread->enabled) break;
 
-      if (f_thread_mutex_lock_try(&thread->mutex->asynchronous) == F_none) {
+      if (f_thread_mutex_lock_try(&thread->mutex.asynchronous) == F_none) {
 
         if (thread->asynchronouss.array[i].state != controller_asynchronous_state_joined) {
           f_thread_join(thread->asynchronouss.array[i].id, 0);
         }
 
-        if (thread->asynchronouss.enabled && thread->asynchronouss.array[i].state) {
+        if (thread->enabled && thread->asynchronouss.array[i].state) {
           if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) {
             thread->asynchronouss.array[i].state = controller_asynchronous_state_joined;
           }
@@ -4096,7 +4139,7 @@ extern "C" {
           controller_macro_cache_action_t_clear(thread->asynchronouss.array[i].cache);
         }
 
-        f_thread_mutex_unlock(&thread->mutex->asynchronous);
+        f_thread_mutex_unlock(&thread->mutex.asynchronous);
       }
     } // for
   }
index 6ecb15c80ba989021748f692c2b3002528c58520..cb26b9706948a5432e47871712c3cf5b2f931c74 100644 (file)
@@ -130,6 +130,23 @@ extern "C" {
 #endif // _di_controller_rule_action_read_
 
 /**
+ * Copy a rule, allocating new space as necessary.
+ *
+ * @param source
+ *   The source rule to copy from.
+ * @param destination
+ *   The destination rule to copy to.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: XXXX().
+ */
+#ifndef _di_controller_rule_copy_
+  extern f_status_t controller_rule_copy(controller_rule_t *source, controller_rule_t *destination) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_copy_
+
+/**
  * Print additional error/warning information in addition to existing error.
  *
  * This is explicitly intended to be used in addition to the error message.
@@ -226,8 +243,6 @@ extern "C" {
 /**
  * Perform an execution of the given rule.
  *
- * @param index
- *   The position in the setting.rules array representing the rule to execute.
  * @param type
  *   The action to perform based on the action type codes.
  *
@@ -242,11 +257,12 @@ extern "C" {
  * @param options
  *   A number using bits to represent specific boolean options.
  *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
- * @param thread
+ * @param thread_data
  *   The thread data.
- * @param asynchronous
- *   Holds the current asynchronous thread information if this is being run from within one.
- *   Set to NULL when this is not being called from within an asynchronous thread.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param rule
+ *   The rule to process.
  *
  * @return
  *   F_none on success.
@@ -258,14 +274,12 @@ extern "C" {
  *   On failure, the individual status for the rule is set to an appropriate error status.
  */
 #ifndef _di_controller_rule_execute_
-  extern f_status_t controller_rule_execute(const f_array_length_t index, const uint8_t type, const uint8_t options, controller_thread_t *thread, controller_asynchronous_t *asynchronous) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_execute(const uint8_t type, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_execute_
 
 /**
  * Perform an execution of the given rule in the foreground.
  *
- * @param index
- *   The index location in the rules running this action.
  * @param type
  *   The item type code.
  * @param action
@@ -286,11 +300,10 @@ extern "C" {
  *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  * @param execute_set
  *   The execute parameter and as settings.
- * @param thread
+ * @param thread_data
  *   The thread data.
- * @param asynchronous
- *   Holds the current asynchronous thread information if this is being run from within one.
- *   Set to NULL when this is not being called from within an asynchronous thread.
+ * @param rule
+ *   The rule to process.
  *
  * @return
  *   F_none on success.
@@ -302,7 +315,7 @@ extern "C" {
  * @see fll_execute_program()
  */
 #ifndef _di_controller_rule_execute_foreground_
-  extern f_status_t controller_rule_execute_foreground(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread, controller_asynchronous_t *asynchronous) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_execute_foreground(const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_data_t thread_data, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_execute_foreground_
 
 /**
@@ -311,8 +324,6 @@ extern "C" {
  * When this is synchronous, this will wait for the PID file to be generated before continuing.
  * When this is asynchronous, this will continue on adding the rule id and action to the asynchronous list.
  *
- * @param index
- *   The index location in the rules running this action.
  * @param type
  *   The item type code.
  * @param action
@@ -350,27 +361,28 @@ extern "C" {
  * @see fll_execute_program()
  */
 #ifndef _di_controller_rule_execute_pid_with_
-  extern f_status_t controller_rule_execute_pid_with(const f_array_length_t index, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_t *thread, controller_asynchronous_t *asynchronous) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_execute_pid_with(const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_thread_data_t thread_data, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_execute_pid_with_
 
 /**
  * Search the already loaded rules to see if one is found.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
+ * Looks up the rules starting from the end so that the latest loaded version of any given rule is found and used first.
+ * The rule thread should be locked before calling this to ensure the rule is not loaded after this search.
+ *
  * @param rule_id
  *   The string identifying the rule.
  *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
  *   "/etc/controller/rules/example/my.rule" would have a rule id of "example/my".
+ * @param thread_data
+ *   The thread data.
  *
  * @return
  *   If found, a valid location within the setting.rules array.
  *   If not found, then setting.rules.used is returned.
  */
 #ifndef _di_controller_rule_find_loaded_
-  extern f_array_length_t controller_rule_find_loaded(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id) f_gcc_attribute_visibility_internal;
+  extern f_array_length_t controller_rule_find_loaded(const f_string_static_t rule_id, controller_thread_data_t thread_data) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_find_loaded_
 
 /**
@@ -521,8 +533,6 @@ 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 action
  *   The action to perform based on the action type codes.
  *
@@ -537,11 +547,10 @@ extern "C" {
  *   If no bits set, then operate normally in a synchronous manner.
  *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  *   If bit controller_rule_option_asynchronous, then run asynchronously.
- * @param thread
+ * @param thread_data
  *   The thread data.
- * @param asynchronous
- *   Holds the current asynchronous thread information if this is being run from within one.
- *   Set to NULL when this is not being called from within an asynchronous thread.
+ * @param cache
+ *   A structure for containing and caching relevant data.
  *
  * @return
  *   F_none on success.
@@ -549,7 +558,7 @@ extern "C" {
  *   F_signal on (exit) signal received.
  */
 #ifndef _di_controller_rule_process_
-  extern f_status_t controller_rule_process(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_t *thread, controller_asynchronous_t *asynchronous) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_process(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;
 #endif // _di_controller_rule_process_
 
 /**
@@ -576,8 +585,10 @@ extern "C" {
  *   If no bits set, then operate normally in a synchronous manner.
  *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  *   If bit controller_rule_option_asynchronous, then run asynchronously.
- * @param thread
+ * @param thread_data
  *   The thread data.
+ * @param cache
+ *   A structure for containing and caching relevant data.
  *
  * @return
  *   F_none on success.
@@ -588,21 +599,17 @@ 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_t *thread) f_gcc_attribute_visibility_internal;
+  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;
 #endif // _di_controller_rule_process_asynchronous_
 
 /**
  * Read the rule file, extracting all valid items.
  *
- * @param data
- *   The program data.
- * @param setting
- *   The controller settings data.
  * @param rule_id
  *   The string identifying the rule.
  *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
  *   "/etc/controller/rules/example/my.rule" would have a rule id of "example/my".
- * @param thread
+ * @param thread_data
  *   The thread data.
  * @param cache
  *   A structure for containing and caching relevant data.
@@ -626,7 +633,7 @@ extern "C" {
  * @see fll_fss_basic_list_read().
  */
 #ifndef _di_controller_rule_read_
-  extern f_status_t controller_rule_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id, controller_thread_t *thread, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_read(const f_string_static_t rule_id, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_read_
 
 /**
@@ -675,8 +682,6 @@ extern "C" {
  *
  * This automatically sets the rule's status to F_complete.
  *
- * @param index
- *   The position in the setting.rules array representing the rule to simulate.
  * @param action
  *   The action to perform based on the action type codes.
  *
@@ -691,11 +696,15 @@ extern "C" {
  *   If no bits set, then operate normally in a synchronous manner.
  *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  *   If bit controller_rule_option_asynchronous, then run asynchronously.
- * @param thread
+ * @param thread_data
  *   The thread data.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param rule
+ *   The rule to process.
  */
 #ifndef _di_controller_rule_simulate_
-  extern void controller_rule_simulate(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+  extern void controller_rule_simulate(const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_simulate_
 
 /**
index b39d6e1c0f9187795dca1be60f06de1e804b8f9d..914f6b4f7b13f26dbfda3e07787888205c4abe0f 100644 (file)
@@ -15,7 +15,7 @@ extern "C" {
     controller_asynchronous_t *asynchronous = (controller_asynchronous_t *) arguments;
     controller_thread_t *thread_main = (controller_thread_t *) asynchronous->thread;
 
-    if (!thread_main->asynchronouss.enabled) {
+    if (!thread_main->enabled) {
       return 0;
     }
 
@@ -47,9 +47,9 @@ extern "C" {
 #ifndef _di_controller_thread_asynchronous_cancel_
   void controller_thread_asynchronous_cancel(controller_thread_t *thread) {
 
-    thread->asynchronouss.enabled = F_false;
+    thread->enabled = F_false;
 
-    f_thread_mutex_lock(&thread->mutex->asynchronous);
+    f_thread_mutex_lock(&thread->mutex.asynchronous);
 
     for (f_array_length_t i = 0; i < thread->asynchronouss.used; ++i) {
 
@@ -75,95 +75,89 @@ extern "C" {
 
     thread->asynchronouss.used = 0;
 
-    f_thread_mutex_unlock(&thread->mutex->asynchronous);
+    f_thread_mutex_unlock(&thread->mutex.asynchronous);
   }
 #endif // _di_controller_thread_asynchronous_cancel_
 
-#ifndef _di_controller_thread_cache_
-  void * controller_thread_cache(void *arguments) {
+#ifndef _di_controller_thread_cleanup_
+  void * controller_thread_cleanup(void *arguments) {
 
-    controller_thread_t *thread = (controller_thread_t *) arguments;
+    controller_thread_data_t *thread_data = (controller_thread_data_t *) arguments;
 
-    const unsigned int interval = thread->data->parameters[controller_parameter_test].result == f_console_result_found ? controller_thread_cache_cleanup_interval_short : controller_thread_cache_cleanup_interval_long;
+    const unsigned int interval = thread_data->data->parameters[controller_parameter_test].result == f_console_result_found ? controller_thread_cache_cleanup_interval_short : controller_thread_cache_cleanup_interval_long;
 
     f_array_length_t i = 0;
 
-    for (; thread->asynchronouss.enabled; ) {
+    for (; thread_data->thread->enabled; ) {
       sleep(interval);
 
-      if (f_thread_mutex_lock_try(&thread->mutex->cache) == F_none) {
-        controller_macro_cache_t_delete_simple((*thread->cache_main));
-        controller_macro_cache_action_t_delete_simple((*thread->cache_action));
+      if (f_thread_mutex_lock_try(&thread_data->thread->mutex.asynchronous) == F_none) {
+        controller_thread_t *thread = &thread_data->thread;
 
-        if (f_thread_mutex_lock_try(&thread->mutex->asynchronous) == F_none) {
+        if (thread->asynchronouss.used) {
+          for (i = 0; i < thread->asynchronouss.used; ++i) {
 
-          if (thread->asynchronouss.used) {
-            for (i = 0; i < thread->asynchronouss.used; ++i) {
+            if (!thread->enabled) break;
+            if (!thread->asynchronouss.array[i].state) continue;
 
-              if (!thread->asynchronouss.enabled) break;
-              if (!thread->asynchronouss.array[i].state) continue;
+            if (f_thread_mutex_lock_try(&thread->asynchronouss.array[i].lock) != F_none) continue;
 
-              if (f_thread_mutex_lock_try(&thread->asynchronouss.array[i].lock) != F_none) continue;
+            if (f_thread_mutex_lock_try(&thread_data->setting->rules.array[thread->asynchronouss.array[i].index].lock) == F_none) {
 
-              if (f_thread_mutex_lock_try(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock) == F_none) {
-
-                if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) {
-                  f_thread_join(thread->asynchronouss.array[i].id, 0);
-                  thread->asynchronouss.array[i].state = controller_asynchronous_state_joined;
-                }
-
-                if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) {
-                  controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]);
+              if (thread->asynchronouss.array[i].state == controller_asynchronous_state_done) {
+                f_thread_join(thread->asynchronouss.array[i].id, 0);
+                thread->asynchronouss.array[i].state = controller_asynchronous_state_joined;
+              }
 
-                  thread->asynchronouss.array[i].state = 0;
-                }
+              if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) {
+                controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]);
 
-                f_thread_condition_signal(&thread->setting->rules.array[thread->asynchronouss.array[i].index].wait);
-                f_thread_mutex_unlock(&thread->setting->rules.array[thread->asynchronouss.array[i].index].lock);
+                thread->asynchronouss.array[i].state = 0;
               }
 
-              f_thread_mutex_unlock(&thread->asynchronouss.array[i].lock);
-            } // for
+              f_thread_condition_signal(&thread_data->setting->rules.array[thread->asynchronouss.array[i].index].wait);
+              f_thread_mutex_unlock(&thread_data->setting->rules.array[thread->asynchronouss.array[i].index].lock);
+            }
 
-            for (i = thread->asynchronouss.used - 1; thread->asynchronouss.used; --i, --thread->asynchronouss.used) {
+            f_thread_mutex_unlock(&thread->asynchronouss.array[i].lock);
+          } // for
 
-              if (!thread->asynchronouss.enabled) break;
+          for (i = thread->asynchronouss.used - 1; thread->asynchronouss.used; --i, --thread->asynchronouss.used) {
 
-              if (f_thread_mutex_lock_try(&thread->asynchronouss.array[i].lock) != F_none) break;
+            if (!thread->enabled) break;
 
-              if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) {
-                controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]);
+            if (f_thread_mutex_lock_try(&thread->asynchronouss.array[i].lock) != F_none) break;
 
-                thread->asynchronouss.array[i].state = 0;
-              }
-              else if (thread->asynchronouss.array[i].state) {
-                f_thread_mutex_unlock(&thread->asynchronouss.array[i].lock);
-                break;
-              }
+            if (thread->asynchronouss.array[i].state == controller_asynchronous_state_joined) {
+              controller_macro_asynchronous_t_delete_simple(thread->asynchronouss.array[i]);
 
+              thread->asynchronouss.array[i].state = 0;
+            }
+            else if (thread->asynchronouss.array[i].state) {
               f_thread_mutex_unlock(&thread->asynchronouss.array[i].lock);
-            } // for
-          }
+              break;
+            }
 
-          if (thread->asynchronouss.enabled && thread->asynchronouss.used < thread->asynchronouss.size) {
-            controller_asynchronouss_resize(thread->asynchronouss.used, &thread->asynchronouss);
-          }
+            f_thread_mutex_unlock(&thread->asynchronouss.array[i].lock);
+          } // for
+        }
 
-          f_thread_mutex_unlock(&thread->mutex->asynchronous);
+        if (thread->enabled && thread->asynchronouss.used < thread->asynchronouss.size) {
+          controller_asynchronouss_resize(thread->asynchronouss.used, &thread->asynchronouss);
         }
 
-        f_thread_mutex_unlock(&thread->mutex->cache);
+        f_thread_mutex_unlock(&thread->mutex.asynchronous);
       }
     } // for
 
     return 0;
   }
-#endif // _di_controller_thread_cache_
+#endif // _di_controller_thread_cleanup_
 
 #ifndef _di_controller_thread_control_
   void * controller_thread_control(void *arguments) {
 
-    controller_thread_t *thread = (controller_thread_t *) arguments;
+    controller_thread_data_t *thread_data = (controller_thread_data_t *) arguments;
 
     // @todo
 
@@ -172,147 +166,158 @@ extern "C" {
 #endif // _di_controller_thread_control_
 
 #ifndef _di_controller_thread_main_
-  f_status_t controller_thread_main(const f_string_static_t entry_name, controller_cache_t *cache, controller_thread_t *thread) {
+  f_status_t controller_thread_main(const f_string_static_t entry_name, controller_data_t *data, controller_setting_t *setting) {
 
-    f_thread_id_t thread_cache = 0;
-    f_thread_id_t thread_control = 0;
-    f_thread_id_t thread_rule = 0;
-    f_thread_id_t thread_signal = 0;
+    f_status_t status = F_none;
 
-    f_status_t status = f_thread_create(0, &thread_signal, &controller_thread_signal, (void *) thread);
+    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);
+
+    status = controller_asynchronouss_increase(&thread.asynchronouss);
 
     if (F_status_is_error_not(status)) {
-      status = f_thread_create(0, &thread_cache, &controller_thread_cache, (void *) thread);
+      status = f_thread_create(0, &thread.id_signal, &controller_thread_signal, (void *) &data_main);
+    }
+
+    if (F_status_is_error_not(status)) {
+      status = f_thread_create(0, &thread.id_cleanup, &controller_thread_cleanup, (void *) &data_main);
     }
 
     if (F_status_is_error(status)) {
-      if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-        controller_error_print_locked(thread->data->error, F_status_set_fine(status), "f_thread_create", F_true, thread);
+      if (data->error.verbosity != f_console_verbosity_quiet) {
+        controller_error_print_locked(data->error, F_status_set_fine(status), "f_thread_create", F_true, &thread);
       }
     }
     else {
-      if (thread->data->parameters[controller_parameter_daemon].result == f_console_result_found) {
-        thread->setting->ready = controller_setting_ready_done;
+      if (data->parameters[controller_parameter_daemon].result == f_console_result_found) {
+        setting->ready = controller_setting_ready_done;
 
-        if (f_file_exists(thread->setting->path_pid.string) == F_true) {
-          if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-            f_thread_mutex_lock(&thread->mutex->print);
+        if (f_file_exists(setting->path_pid.string) == F_true) {
+          if (data->error.verbosity != f_console_verbosity_quiet) {
+            f_thread_mutex_lock(&thread.mutex.print);
 
-            fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]);
-            fprintf(thread->data->error.to.stream, "%s%sThe pid file '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s);
-            fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, thread->setting->path_pid.string, thread->data->error.notable.after->string);
-            fprintf(thread->data->error.to.stream, "%s' must not already exist.%s%c", thread->data->error.context.before->string, thread->data->error.context.after->string, f_string_eol_s[0]);
+            fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
+            fprintf(data->error.to.stream, "%s%sThe pid file '", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
+            fprintf(data->error.to.stream, "%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, setting->path_pid.string, data->error.notable.after->string);
+            fprintf(data->error.to.stream, "%s' must not already exist.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
 
-            f_thread_mutex_unlock(&thread->mutex->print);
+            f_thread_mutex_unlock(&thread.mutex.print);
           }
 
-          thread->setting->ready = controller_setting_ready_abort;
+          setting->ready = controller_setting_ready_abort;
           status = F_status_set_error(F_available_not);
         }
       }
       else {
-        f_thread_mutex_lock(&thread->mutex->cache);
 
-        status = controller_entry_read(*thread->data, *thread->setting, entry_name, cache, &thread->setting->entry);
+        status = controller_entry_read(entry_name, data_main, &thread.asynchronouss.array[0].cache);
 
         if (F_status_is_error(status)) {
-          thread->setting->ready = controller_setting_ready_fail;
+          setting->ready = controller_setting_ready_fail;
         }
         else if (status != F_signal && status != F_child) {
-          status = controller_preprocess_entry(cache, thread);
+          status = controller_preprocess_entry(data_main, &thread.asynchronouss.array[0].cache);
         }
 
         if (F_status_is_error_not(status) && status != F_signal && status != F_child) {
-          if (thread->data->parameters[controller_parameter_validate].result == f_console_result_none || thread->data->parameters[controller_parameter_test].result == f_console_result_found) {
+          if (data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found) {
 
-            if (f_file_exists(thread->setting->path_pid.string) == F_true) {
-              if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-                f_thread_mutex_lock(&thread->mutex->print);
+            if (f_file_exists(setting->path_pid.string) == F_true) {
+              if (data->error.verbosity != f_console_verbosity_quiet) {
+                f_thread_mutex_lock(&thread.mutex.print);
 
-                fprintf(thread->data->error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(thread->data->error.to.stream, "%s%sThe pid file '", thread->data->error.context.before->string, thread->data->error.prefix ? thread->data->error.prefix : f_string_empty_s);
-                fprintf(thread->data->error.to.stream, "%s%s%s%s", thread->data->error.context.after->string, thread->data->error.notable.before->string, thread->setting->path_pid.string, thread->data->error.notable.after->string);
-                fprintf(thread->data->error.to.stream, "%s' must not already exist.%s%c", thread->data->error.context.before->string, thread->data->error.context.after->string, f_string_eol_s[0]);
+                fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(data->error.to.stream, "%s%sThe pid file '", data->error.context.before->string, data->error.prefix ? data->error.prefix : f_string_empty_s);
+                fprintf(data->error.to.stream, "%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, setting->path_pid.string, data->error.notable.after->string);
+                fprintf(data->error.to.stream, "%s' must not already exist.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol_s[0]);
 
-                f_thread_mutex_unlock(&thread->mutex->print);
+                f_thread_mutex_unlock(&thread.mutex.print);
               }
 
-              thread->setting->ready = controller_setting_ready_fail;
+              setting->ready = controller_setting_ready_fail;
               status = F_status_set_error(F_available_not);
             }
             else {
-              status = controller_process_entry(cache, thread);
+              status = controller_process_entry(data_main, &thread.asynchronouss.array[0].cache);
 
               if (F_status_is_error(status)) {
-                thread->setting->ready = controller_setting_ready_fail;
+                setting->ready = controller_setting_ready_fail;
               }
               else if (status == F_signal || status == F_child) {
-                thread->setting->ready = controller_setting_ready_abort;
+                setting->ready = controller_setting_ready_abort;
               }
               else {
-                thread->setting->ready = controller_setting_ready_done;
+                setting->ready = controller_setting_ready_done;
               }
             }
           }
         }
 
         if (status == F_child) {
+          controller_thread_delete_simple(&thread);
+
           return F_child;
         }
-
-        f_thread_mutex_unlock(&thread->mutex->cache);
       }
     }
 
-    if (status != F_signal && thread->setting->signal) {
+    if (status != F_signal && setting->signal) {
       status = F_signal;
     }
 
     // only make the rule and control threads available once any/all pre-processing and is completed.
     if (F_status_is_error_not(status) && status != F_signal && status != F_child) {
 
-      if (thread->data->parameters[controller_parameter_validate].result == f_console_result_none) {
-        controller_rule_wait_all(thread);
+      if (data->parameters[controller_parameter_validate].result == f_console_result_none) {
+        controller_rule_wait_all(&thread);
 
-        status = f_thread_create(0, &thread_rule, &controller_thread_rule, (void *) thread);
+        status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &data_main);
 
         if (F_status_is_error_not(status)) {
-          status = f_thread_create(0, &thread_control, &controller_thread_control, (void *) thread);
+          status = f_thread_create(0, &thread.id_control, &controller_thread_control, (void *) &data_main);
         }
 
         if (F_status_is_error(status)) {
-          if (thread->data->error.verbosity != f_console_verbosity_quiet) {
-            controller_error_print_locked(thread->data->error, F_status_set_fine(status), "f_thread_create", F_true, thread);
+          if (data->error.verbosity != f_console_verbosity_quiet) {
+            controller_error_print_locked(data->error, F_status_set_fine(status), "f_thread_create", F_true, &thread);
           }
         }
       }
     }
 
     if (status == F_child) {
+      controller_thread_delete_simple(&thread);
+
       return F_child;
     }
 
-    if (F_status_is_error_not(status) && status != F_signal && (thread->data->parameters[controller_parameter_validate].result == f_console_result_none || thread->data->parameters[controller_parameter_test].result == f_console_result_found)) {
+    // @todo consider f_thread_detach() over f_thread_join() when exiting.
+    if (F_status_is_error_not(status) && status != F_signal && (data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found)) {
 
       // wait until signal thread exits, which happens on any termination signal.
-      f_thread_join(thread_signal, 0);
+      f_thread_join(thread.id_signal, 0);
     }
     else {
-      f_thread_cancel(thread_signal);
-      f_thread_join(thread_signal, 0);
+      f_thread_cancel(thread.id_signal);
+      f_thread_join(thread.id_signal, 0);
     }
 
-    if (thread->asynchronouss.enabled) {
-      controller_thread_asynchronous_cancel(thread);
+    if (thread.enabled) {
+      controller_thread_asynchronous_cancel(&thread);
     }
 
-    f_thread_cancel(thread_cache);
-    f_thread_cancel(thread_control);
-    f_thread_cancel(thread_rule);
+    // @todo when cancelling, make sure that whatever has a lock, no longer has any lock.
+    f_thread_cancel(thread.id_cleanup);
+    f_thread_cancel(thread.id_control);
+    f_thread_cancel(thread.id_rule);
+
+    f_thread_join(thread.id_cleanup, 0);
+    f_thread_join(thread.id_control, 0);
+    f_thread_join(thread.id_rule, 0);
 
-    f_thread_join(thread_cache, 0);
-    f_thread_join(thread_control, 0);
-    f_thread_join(thread_rule, 0);
+    controller_processs_delete_simple(&processs);
+    controller_thread_delete_simple(&thread);
 
     if (F_status_is_error(status)) {
       return F_status_set_error(F_failure);
@@ -329,11 +334,11 @@ extern "C" {
 #ifndef _di_controller_thread_rule_
   void * controller_thread_rule(void *arguments) {
 
-    controller_thread_t *thread = (controller_thread_t *) arguments;
+    controller_thread_data_t *thread_data = (controller_thread_data_t *) arguments;
 
     // @todo
-    // f_thread_mutex_lock(&thread->mutex->rule);
-    // f_thread_mutex_unlock(&thread->mutex->rule);
+    // f_thread_mutex_lock(&thread_data->mutex.rule);
+    // f_thread_mutex_unlock(&thread_data->mutex.rule);
 
     return 0;
   }
@@ -342,17 +347,17 @@ extern "C" {
 #ifndef _di_controller_thread_signal_
   void * controller_thread_signal(void *arguments) {
 
-    controller_thread_t *thread = (controller_thread_t *) arguments;
+    controller_thread_data_t *thread_data = (controller_thread_data_t *) arguments;
 
-    for (int signal = 0; thread->asynchronouss.enabled; ) {
+    for (int signal = 0; thread_data->thread->enabled; ) {
 
-      sigwait(&thread->data->signal.set, &signal);
+      sigwait(&thread_data->data->signal.set, &signal);
 
-      if (thread->data->parameters[controller_parameter_interruptable].result == f_console_result_found) {
+      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->setting->signal = signal;
+          thread_data->setting->signal = signal;
 
-          controller_thread_asynchronous_cancel(thread);
+          controller_thread_asynchronous_cancel(thread_data->thread);
           break;
         }
       }
index 2e062991195099aeb7f3e406ee28490b9d9fa4d2..99e7f80480825653bc9973a058e9a119f55b472b 100644 (file)
@@ -37,25 +37,25 @@ extern "C" {
 #endif // _di_controller_thread_asynchronous_cancel_
 
 /**
- * Thread for periodically clearing cache if cache is not busy.
+ * Thread for periodically cleanup data when not busy.
  *
  * @param arguments
  *   The thread arguments.
- *   Must be of type controller_thread_t.
+ *   Must be of type controller_thread_data_t.
  *
  * @return
  *   0, always.
  */
-#ifndef _di_controller_thread_cache_
-  extern void * controller_thread_cache(void *arguments) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_thread_cache_
+#ifndef _di_controller_thread_cleanup_
+  extern void * controller_thread_cleanup(void *arguments) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_thread_cleanup_
 
 /**
  * Thread for handling control requests and responses.
  *
  * @param arguments
  *   The thread arguments.
- *   Must be of type controller_thread_t.
+ *   Must be of type controller_thread_data_t.
  *
  * @return
  *   0, always.
@@ -69,10 +69,10 @@ extern "C" {
  *
  * @param entry_name
  *   The entry name string.
- * @param cache
- *   The main/global cache to use.
- * @param thread
- *   The thread data.
+ * @param data
+ *   The controller data.
+ * @param setting
+ *   The controller settings data.
  *
  * @return
  *   F_none on success.
@@ -81,7 +81,7 @@ extern "C" {
  *   F_failure (with error bit) on any failure.
  */
 #ifndef _di_controller_thread_main_
-  extern f_status_t controller_thread_main(const f_string_static_t entry_name, controller_cache_t *cache, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_thread_main(const f_string_static_t entry_name, controller_data_t *data, controller_setting_t *setting) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_thread_main_
 
 /**
@@ -89,7 +89,7 @@ extern "C" {
  *
  * @param arguments
  *   The thread arguments.
- *   Must be of type controller_thread_t.
+ *   Must be of type controller_thread_data_t.
  *
  * @return
  *   0, always.
@@ -105,7 +105,7 @@ extern "C" {
  *
  * @param arguments
  *   The thread arguments.
- *   Must be of type controller_thread_t.
+ *   Must be of type controller_thread_data_t.
  *
  * @return
  *   0, always.