]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Mon, 15 Mar 2021 00:56:53 +0000 (19:56 -0500)
committerKevin Day <thekevinday@gmail.com>
Mon, 15 Mar 2021 00:56:53 +0000 (19:56 -0500)
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

index 789070ba143dc1f16bd15ca50e13c0b5ab3063a5..0cc1f62be90a111df387b3c52cc00e0d6728c61d 100644 (file)
  * This program provides system service management, much like sysvcontroller and controllerng.
  * This program can be controlled from user-space via the "control" program.
  *
- * @todo research containers and build in container support into this, providing "container" appropriate verbiage for individual rules.
- * @todo research namespaces and user_namespaces, they may be important to support.
- *
  * @todo Implement "exit" files that are the opposite of "entry" files whereas rules specified within are all called via the "stop" action type.
  *       This would then allow for switching modes.
+ *
+ * @fixme the entire program needs to check the return status on all locks (mutex, r/w locks, etc..) and if there is a problem, print a message and wait (but ideally allow for signals, if enabled).
+ *
+ * @todo This currently only exits on memory error.
+ *       For a production "init" program, this should continue to run...so it needs to sleep and wait for resources to become available.
  */
 #ifndef _controller_h
 
index bd3c4d68590a7cd3631d3fd72aafc68020f1366e..3ed2c031b52b2534394a005dfe3753c2e612ac2e 100644 (file)
@@ -11,7 +11,6 @@ extern "C" {
     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_
 
@@ -135,24 +134,26 @@ extern "C" {
   }
 #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) {
+#ifndef _di_controller_error_print_
+  void controller_error_print(const fll_error_print_t print, 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);
+    if (print.verbosity != f_console_verbosity_quiet) {
+      f_thread_mutex_lock(&thread->lock.print);
 
-      fll_error_print(error, status, function, fallback);
+      fll_error_print(print, status, function, fallback);
 
-      f_thread_mutex_unlock(&thread->mutex.print);
+      f_thread_mutex_unlock(&thread->lock.print);
     }
   }
-#endif // _di_controller_error_print_locked_
+#endif // _di_controller_error_print_
 
 #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_lock_delete(&process->lock);
+    f_thread_lock_attribute_delete(&process->attribute);
     f_thread_condition_delete(&process->wait);
   }
 #endif // _di_controller_process_delete_simple_
@@ -232,24 +233,24 @@ extern "C" {
 #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_string_maps_resize(0, &rule->define);
+    f_string_maps_resize(0, &rule->parameter);
+
+    f_string_dynamics_resize(0, &rule->environment);
+    f_string_dynamics_resize(0, &rule->need);
+    f_string_dynamics_resize(0, &rule->want);
+    f_string_dynamics_resize(0, &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_capability_delete(&rule->capability);
 
     controller_rule_items_delete_simple(&rule->items);
@@ -259,7 +260,7 @@ extern "C" {
 #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);
+    controller_rule_actions_delete_simple(&item->actions);
   }
 #endif // _di_controller_rule_item_delete_simple_
 
@@ -343,10 +344,16 @@ extern "C" {
 #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);
+    f_thread_mutex_delete(&thread->lock.print);
+
+    f_thread_lock_delete(&thread->lock.asynchronous);
+    f_thread_lock_attribute_delete(&thread->lock.asynchronous_attribute);
+
+    f_thread_lock_delete(&thread->lock.process);
+    f_thread_lock_attribute_delete(&thread->lock.process_attribute);
+
+    f_thread_lock_delete(&thread->lock.rule);
+    f_thread_lock_attribute_delete(&thread->lock.rule_attribute);
 
     controller_asynchronouss_resize(0, &thread->asynchronouss);
   }
index c284b48466aebee10afde7e9fec5853e86039f08..df59d78438bc4d051ea10ad467b0672779eef463 100644 (file)
@@ -324,19 +324,28 @@ extern "C" {
  * 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_
+#ifndef _di_controller_lock_t_
   typedef struct {
-    f_thread_mutex_t asynchronous;
     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_lock_t asynchronous;
+    f_thread_lock_attribute_t asynchronous_attribute;
+
+    f_thread_lock_t process;
+    f_thread_lock_attribute_t process_attribute;
+
+    f_thread_lock_t rule;
+    f_thread_lock_attribute_t rule_attribute;
+  } controller_lock_t;
+
+  #define controller_lock_t_initialize { \
     f_thread_mutex_t_initialize, \
-    f_thread_mutex_t_initialize \
+    f_thread_lock_t_initialize, \
+    f_thread_lock_attribute_t_initialize, \
+    f_thread_lock_t_initialize, \
+    f_thread_lock_attribute_t_initialize, \
+    f_thread_lock_t_initialize, \
+    f_thread_lock_attribute_t_initialize \
   }
 #endif // _di_controller_mutex_t_
 
@@ -613,17 +622,20 @@ extern "C" {
  */
 #ifndef _di_controller_process_t_
   typedef struct {
-    f_status_t status;
     f_string_dynamic_t id;
 
-    f_thread_mutex_t lock;
+    f_status_t status;
+
+    f_thread_lock_t lock;
+    f_thread_lock_attribute_t attribute;
     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_known_not, \
+    f_thread_lock_t_initialize, \
+    f_thread_lock_attribute_t_initialize, \
     f_thread_condition_t_initialize, \
   }
 
@@ -941,9 +953,10 @@ extern "C" {
 
   typedef struct {
     f_thread_id_t id;
-    f_thread_mutex_t lock;
-    f_array_length_t index;
+    f_thread_lock_t lock;
+    f_thread_lock_attribute_t attribute;
 
+    f_array_length_t index;
     uint8_t state;
     uint8_t action;
     uint8_t options;
@@ -951,22 +964,22 @@ extern "C" {
 
     f_array_lengths_t stack;
     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, \
+    f_thread_lock_t_initialize, \
+    f_thread_lock_attribute_t_initialize, \
     0, \
     0, \
     0, \
     0, \
     0, \
     f_array_lengths_t_initialize, \
-    controller_cache_t_initialize, \
-    controller_rule_t_initialize \
+    controller_cache_t_initialize \
   }
 
+  // @fixme remove the clear() macros..clear isn't safe when mixing in mutexes and whatnot that cannot be cleared.
   #define controller_macro_asynchronous_t_clear(asynchronous) \
     f_macro_thread_id_t_clear(asynchronous.id) \
     asynchronous.index = 0; \
@@ -975,8 +988,7 @@ extern "C" {
     asynchronous.options = 0; \
     asynchronous.child = 0; \
     f_macro_array_lengths_t_clear(asynchronous.stack) \
-    controller_macro_cache_t_clear(asynchronous.cache) \
-    controller_macro_rule_t_clear(asynchronous.rule)
+    controller_macro_cache_t_clear(asynchronous.cache)
 #endif // _di_controller_asynchronous_t_
 
 #ifndef _di_controller_asynchronouss_t_
@@ -1008,7 +1020,7 @@ extern "C" {
     f_thread_id_t id_rule;
     f_thread_id_t id_signal;
 
-    controller_mutex_t mutex;
+    controller_lock_t lock;
     controller_asynchronouss_t asynchronouss;
   } controller_thread_t;
 
@@ -1018,17 +1030,17 @@ extern "C" {
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
-    controller_mutex_t_initialize, \
+    controller_lock_t_initialize, \
     controller_asynchronouss_t_initialize \
   }
 
-  #define controller_macro_thread_t_initialize(mutex, asynchronouss) { \
+  #define controller_macro_thread_t_initialize(lock, asynchronouss) { \
     F_true, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize. \
-    mutex, \
+    lock, \
     asynchronouss \
   }
 
@@ -1038,7 +1050,6 @@ extern "C" {
     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_
 
@@ -1234,9 +1245,9 @@ extern "C" {
  *
  * @see fll_error_print()
  */
-#ifndef _di_controller_error_print_locked_
-  extern 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) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_error_print_locked_
+#ifndef _di_controller_error_print_
+  extern void controller_error_print(const fll_error_print_t print, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_error_print_
 
 /**
  * Fully deallocate all memory for the given process without caring about return status.
index 3b096b4d0d356efeb58f78e24dac74afc37fa85e..2fe90a915878ee606198651dfb664c6edd3121a7 100644 (file)
@@ -69,14 +69,28 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_append", F_true);
+      if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_append", F_true);
+
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
+      }
+
       return status;
     }
 
     status = f_string_dynamic_terminate_after(&cache->action.name_file);
 
     if (F_status_is_error(status)) {
-      fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+      if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+        fll_error_print(thread_data.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
+      }
+
       return status;
     }
 
@@ -95,13 +109,25 @@ extern "C" {
     status = f_file_stream_open(path, 0, &file);
 
     if (F_status_is_error(status)) {
-      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);
+      if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+        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);
+
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
+      }
     }
     else {
       status = f_file_stream_read(file, 1, &cache->buffer_file);
 
       if (F_status_is_error(status)) {
-        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);
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+          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);
+
+          f_thread_mutex_unlock(&thread_data.thread->lock.print);
+        }
       }
     }
 
@@ -113,7 +139,13 @@ extern "C" {
       status = f_file_stat(path, F_true, &stat_file);
 
       if (F_status_is_error(status)) {
-        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);
+        if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+          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);
+
+          f_thread_mutex_unlock(&thread_data.thread->lock.print);
+        }
       }
       else {
         cache->timestamp.seconds = stat_file.st_ctim.tv_sec;
@@ -219,6 +251,24 @@ extern "C" {
   }
 #endif // _di_controller_file_pid_delete_
 
+#ifndef _di_controller_find_process_
+  f_status_t controller_find_process(const f_string_static_t id, const controller_processs_t processs, f_array_length_t *at) {
+
+    if (!id.used) return F_none;
+    if (!processs.used) return F_false;
+
+    for (f_array_length_t i = 0; i < processs.used; ++i) {
+
+      if (fl_string_dynamic_compare(id, processs.array[i].id) == F_equal_to) {
+        *at = i;
+        return F_true;
+      }
+    } // for
+
+    return F_false;
+  }
+#endif // _di_controller_find_process_
+
 #ifndef _di_controller_get_id_user_
   f_status_t controller_get_id_user(const f_string_static_t buffer, const f_string_range_t range, controller_cache_t *cache, uid_t *id) {
     f_number_unsigned_t number = 0;
@@ -317,16 +367,28 @@ extern "C" {
 
         // always return immediately on memory errors.
         if (F_status_set_fine(status) == F_memory_not) {
-          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);
+          if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
 
-          controller_entry_error_print(thread_data.data->error, cache->action);
+            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_cache(thread_data.data->error, cache->action);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
+          }
 
           return status;
         }
 
-        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);
+        if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+          f_thread_mutex_lock(&thread_data.thread->lock.print);
 
-        controller_entry_error_print(thread_data.data->warning, cache->action);
+          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_cache(thread_data.data->warning, cache->action);
+
+          f_thread_mutex_unlock(&thread_data.thread->lock.print);
+        }
 
         status = F_none;
       }
@@ -363,7 +425,7 @@ 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(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, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true, thread_data.thread);
       return status;
     }
 
@@ -378,8 +440,7 @@ extern "C" {
     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(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);
+      controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
       return status;
     }
@@ -400,8 +461,7 @@ extern "C" {
         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(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);
+          controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
           return status2;
         }
@@ -410,12 +470,16 @@ extern "C" {
 
           if (thread_data.setting->ready == controller_setting_ready_wait) {
             if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               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(thread_data.data->warning, cache->action);
+              controller_entry_error_print_cache(thread_data.data->warning, cache->action);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
           }
 
@@ -439,13 +503,17 @@ extern "C" {
 
                 if (cache->ats.array[j] == i) {
                   if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                    f_thread_mutex_lock(&thread_data.thread->lock.print);
+
                     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(thread_data.data->error, cache->action);
+                    controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+                    f_thread_mutex_unlock(&thread_data.thread->lock.print);
+                  }
 
                   if (F_status_is_error_not(status)) {
                     status = F_status_set_error(F_recurse);
@@ -461,8 +529,7 @@ 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(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);
+                controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status2), "f_macro_array_lengths_t_increase_by", F_true, thread_data.thread);
 
                 return status2;
               }
@@ -487,8 +554,7 @@ extern "C" {
               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(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);
+                controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
                 return status2;
               }
@@ -500,13 +566,17 @@ extern "C" {
           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) {
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
+
                 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(thread_data.data->error, cache->action);
+                controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
+              }
 
               if (F_status_is_error_not(status)) {
                 status = F_status_set_error(F_valid_not);
@@ -540,8 +610,7 @@ extern "C" {
         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(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);
+          controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
           return status2;
         }
@@ -572,6 +641,8 @@ extern "C" {
 
     controller_entry_actions_t *actions = 0;
 
+    controller_process_t *process = 0;
+
     const bool simulate = thread_data.data->parameters[controller_parameter_test].result == f_console_result_found;
 
     cache->ats.used = 0;
@@ -590,8 +661,7 @@ 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(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);
+      controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true, thread_data.thread);
 
       return status;
     }
@@ -607,19 +677,24 @@ extern "C" {
     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(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);
+      controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
       return status;
     }
 
     if (simulate) {
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
+
       fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
       fprintf(thread_data.data->output.stream, "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]);
+
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
     }
 
+    // @todo this function should maintain a read lock on "thread_data.thread->lock.rule", but it must immediately release the read lock before it can do a write lock (which is unfortunate).
+    //       I would much rather gain the write lock and then release the read lock, but that is likely not possible.
     for (;;) {
 
       actions = &thread_data.setting->entry.items.array[cache->ats.array[at_i]].actions;
@@ -637,8 +712,7 @@ extern "C" {
         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(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);
+          controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
           return status;
         }
@@ -648,6 +722,8 @@ extern "C" {
           if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
 
             if (simulate) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
               fprintf(thread_data.data->output.stream, "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);
@@ -660,10 +736,14 @@ extern "C" {
               }
 
               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]);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
             else if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) {
 
               if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
+
                 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);
@@ -678,13 +758,17 @@ extern "C" {
                 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(thread_data.data->error, cache->action);
+                controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
+              }
 
               return F_status_is_error(F_require);
             }
             else if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               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);
@@ -700,11 +784,15 @@ extern "C" {
               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(thread_data.data->warning, cache->action);
+              controller_entry_error_print_cache(thread_data.data->warning, cache->action);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
           }
           else {
             if (simulate) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
               fprintf(thread_data.data->output.stream, "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);
@@ -717,8 +805,12 @@ extern "C" {
               }
 
               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]);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
             else if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               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);
@@ -732,7 +824,9 @@ extern "C" {
               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(thread_data.data->warning, cache->action);
+              controller_entry_error_print_cache(thread_data.data->warning, cache->action);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
           }
 
@@ -744,10 +838,14 @@ extern "C" {
           if (thread_data.setting->ready == controller_setting_ready_wait) {
 
             if (simulate) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
               fprintf(thread_data.data->output.stream, "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]);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
             else {
               controller_perform_ready(thread_data, cache);
@@ -757,10 +855,14 @@ extern "C" {
             thread_data.setting->ready = controller_setting_ready_yes;
           }
           else if (simulate) {
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
+
             fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
             fprintf(thread_data.data->output.stream, "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]);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
@@ -769,13 +871,17 @@ extern "C" {
 
             // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
             if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               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(thread_data.data->error, cache->action);
+              controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
+            }
 
             return F_status_is_error(F_critical);
           }
@@ -783,8 +889,7 @@ 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(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);
+            controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true, thread_data.thread);
 
             return status;
           }
@@ -807,17 +912,20 @@ extern "C" {
           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(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);
+            controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
             return status;
           }
 
           if (simulate) {
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
+
             fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
             fprintf(thread_data.data->output.stream, "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]);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
 
           // exit inner loop to force restarting and start processing the requested item.
@@ -828,8 +936,7 @@ extern "C" {
           status = controller_rules_increase(&thread_data.setting->rules);
 
           if (F_status_is_error(status)) {
-            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);
+            controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_rules_increase", F_true, thread_data.thread);
 
             return status;
           }
@@ -844,23 +951,27 @@ 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;
 
-          f_thread_mutex_lock(&thread_data.thread->mutex.rule);
+          f_thread_lock_read(&thread_data.thread->lock.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);
-          }
+          const f_array_length_t used = thread_data.setting->rules.used;
+
+          f_thread_unlock(&thread_data.thread->lock.rule);
 
           if (simulate) {
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
+
             fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
             fprintf(thread_data.data->output.stream, "%s entry item rule '", actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule ? "Processing" : "Considering");
             fprintf(thread_data.data->output.stream, "%s%s%s", thread_data.data->context.set.title.before->string, rule_id.string, thread_data.data->context.set.title.after->string);
             fprintf(thread_data.data->output.stream, "'.%c", f_string_eol_s[0]);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
 
           // the rule is not yet loaded, ensure that it is loaded.
-          if (at == thread_data.setting->rules.used) {
+          if (at == used) {
 
             // rule execution will re-use the existing cache, so save the current cache.
             const f_array_length_t cache_line_action = cache->action.line_action;
@@ -878,6 +989,8 @@ extern "C" {
             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);
 
+            f_thread_lock_write(&thread_data.thread->lock.rule);
+
             status = controller_rule_read(rule_id, thread_data, cache, &thread_data.setting->rules.array[thread_data.setting->rules.used]);
 
             // restore cache.
@@ -897,10 +1010,17 @@ extern "C" {
             cache->action.line_item = cache_line_item;
 
             if (F_status_is_error(status)) {
-              controller_entry_error_print(thread_data.data->error, cache->action);
+
+              if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+                controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
+              }
 
               if (!simulate) {
-                f_thread_mutex_unlock(&thread_data.thread->mutex.rule);
+                f_thread_unlock(&thread_data.thread->lock.rule);
 
                 break;
               }
@@ -909,10 +1029,10 @@ extern "C" {
               thread_data.setting->rules.used++;
             }
 
-            f_thread_mutex_unlock(&thread_data.thread->mutex.rule);
+            f_thread_unlock(&thread_data.thread->lock.rule);
           }
 
-          if (F_status_is_error_not(status)) {
+          if (F_status_is_error_not(status) && actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
 
             // rule execution will re-use the existing cache, so save the current cache.
             const f_array_length_t cache_line_action = cache->action.line_action;
@@ -930,39 +1050,62 @@ extern "C" {
             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;
+            rule_options = 0;
 
-              if (simulate) {
-                rule_options |= controller_rule_option_simulate;
-              }
+            if (simulate) {
+              rule_options |= controller_rule_option_simulate;
+            }
 
-              if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) {
-                rule_options |= controller_rule_option_require;
-              }
+            if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) {
+              rule_options |= controller_rule_option_require;
+            }
 
-              if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_wait) {
-                rule_options |= controller_rule_option_wait;
-              }
+            if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_wait) {
+              rule_options |= controller_rule_option_wait;
+            }
 
-              // @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;
 
-              if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_asynchronous) {
-                rule_options |= controller_rule_option_asynchronous;
+              // @todo this function will need to do the same process prep work as is done right before controller_rule_process().
+              status = controller_rule_process_asynchronous(at, controller_rule_action_type_start, rule_options, thread_data, cache);
+            }
+            else {
 
-                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_data, cache);
+              f_array_length_t at_process = 0;
+
+              f_thread_lock_read(&thread_data.thread->lock.process);
+
+              if (controller_find_process(rule_id, *thread_data.processs, &at_process) == F_false) {
+                f_thread_unlock(&thread_data.thread->lock.process);
+                f_thread_lock_write(&thread_data.thread->lock.process);
+
+                status = controller_processs_increase(thread_data.processs);
+
+                if (F_status_is_error(status)) {
+                  controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_processs_increase", F_true, thread_data.thread);
+                }
+                else {
+                  at_process = thread_data.processs->used;
+                }
               }
 
-              if (status == F_child || status == F_signal) break;
+              f_thread_unlock(&thread_data.thread->lock.process);
 
-              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_not(status)) {
+                f_thread_lock_write(&thread_data.processs->array[at_process].lock);
+
+                controller_rule_t rule = controller_rule_t_initialize;
+
+                status = controller_rule_copy(thread_data.setting->rules.array[at], &rule);
+
+                f_thread_condition_signal(&thread_data.processs->array[at_process].wait);
+                f_thread_unlock(&thread_data.processs->array[at_process].lock);
+
+                status = controller_rule_process(rule, at_process, controller_rule_action_type_start, rule_options, thread_data, cache);
+
+                controller_rule_delete_simple(&rule);
+              }
             }
 
             // restore cache.
@@ -980,10 +1123,17 @@ extern "C" {
 
             cache->action.line_action = cache_line_action;
             cache->action.line_item = cache_line_item;
+
+            if (status == F_child || status == F_signal) break;
           }
 
+          // @todo this may need to be relocated (currently it may result in multiple error messages due to other messages being printed above.)
           if (F_status_is_error(status)) {
-            controller_entry_error_print(thread_data.data->error, cache->action);
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
+
+            controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
             if (!simulate || F_status_set_fine(status) == F_memory_not) {
               break;
@@ -1005,6 +1155,8 @@ extern "C" {
               code = controller_string_stop_s;
             }
 
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
+
             fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
             fprintf(thread_data.data->output.stream, "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);
@@ -1013,6 +1165,8 @@ extern "C" {
             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]);
+
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
 
           if (actions->array[cache->ats.array[at_j]].code == controller_entry_timeout_code_kill) {
@@ -1031,13 +1185,17 @@ extern "C" {
 
             // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
             if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               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(thread_data.data->error, cache->action);
+              controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
+            }
 
             return F_status_is_error(F_critical);
           }
@@ -1046,12 +1204,16 @@ extern "C" {
             thread_data.setting->failsafe_rule_id = actions->array[cache->ats.array[at_j]].number;
 
             if (simulate) {
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
+
               fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
               fprintf(thread_data.data->output.stream, "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]);
+
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
             }
           }
         }
@@ -1090,8 +1252,7 @@ extern "C" {
         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(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);
+          controller_entry_error_print(thread_data.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data.thread);
 
           break;
         }
@@ -1103,7 +1264,11 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status) && simulate) {
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
+
       fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
+
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
     }
 
     return status;
@@ -1188,6 +1353,8 @@ extern "C" {
   }
 #endif // _di_controller_validate_has_graph_
 
+controller_processs_t
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 38eb59a2d80baf25e40222732675ecf7fc192479..f6f9a745a5b9a972e423e1545084ae6043cab1b4 100644 (file)
@@ -157,6 +157,30 @@ extern "C" {
 #endif // _di_controller_file_pid_delete_
 
 /**
+ * Find an existing process.
+ *
+ * Do not confuse this with a process in the context of a PID.
+ * This is a stucture for the current processing of some rule.
+ *
+ * This does not do any locking or unlocking for the processs data, be sure to lock appropriately before and after calling this.
+ *
+ * @param id
+ *   The (rule) id to find.
+ * @param processs
+ *   The array of processes to.
+ * @param at
+ *   The location within processs the id was found.
+ *
+ * @return
+ *   F_none if not given a valid id to search.
+ *   F_false if there is no process found.
+ *   F_true if there is a process found (address is stored in "at").
+ */
+#ifndef _di_controller_find_process_
+  f_status_t controller_find_process(const f_string_static_t id, const controller_processs_t processs, f_array_length_t *at) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_find_process_
+
+/**
  * Convert the string from a string representation of an ID or a user name into the numeric representation of that ID or user name.
  *
  * @param buffer
@@ -215,6 +239,8 @@ extern "C" {
  *
  * This prints messages on errors.
  *
+ * This does not do any locking or unlocking for the setting data, be sure to lock appropriately before and after calling this.
+ *
  * @param thread_data
  *   The thread data.
  * @param cache
index 0834af0eeb3a13d53699fa0f0718e5984908dabe..77eb662b8d8afc92662fad7f40221c4efdb6f42c 100644 (file)
@@ -541,37 +541,49 @@ extern "C" {
 #endif // _di_controller_entry_actions_read_
 
 #ifndef _di_controller_entry_error_print_
-  void controller_entry_error_print(const fll_error_print_t output, const controller_cache_action_t cache) {
-
-    if (output.verbosity != f_console_verbosity_quiet) {
-      fprintf(output.to.stream, "%c", f_string_eol_s[0]);
-      fprintf(output.to.stream, "%s%sWhile processing ", output.context.before->string, output.prefix ? output.prefix : f_string_empty_s);
-
-      if (cache.name_action.used) {
-        fprintf(output.to.stream, "action '");
-        fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_action.string, output.notable.after->string);
-        fprintf(output.to.stream, "%s' on line ", output.context.before->string);
-        fprintf(output.to.stream, "%s%s%llu%s", output.context.after->string, output.notable.before->string, cache.line_action, output.notable.after->string);
-        fprintf(output.to.stream, "%s for ", output.context.before->string);
-      }
+  void controller_entry_error_print(const fll_error_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) {
 
-      if (cache.name_item.used) {
-        fprintf(output.to.stream, "entry item '");
-        fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_item.string, output.notable.after->string);
-        fprintf(output.to.stream, "%s' on line ", output.context.before->string);
-        fprintf(output.to.stream, "%s%s%llu%s", output.context.after->string, output.notable.before->string, cache.line_item, output.notable.after->string);
-        fprintf(output.to.stream, "%s for ", output.context.before->string);
-      }
+    if (print.verbosity != f_console_verbosity_quiet) {
+      f_thread_mutex_lock(&thread->lock.print);
 
-      if (cache.name_file.used) {
-        fprintf(output.to.stream, "file '");
-        fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_file.string, output.notable.after->string);
-        fprintf(output.to.stream, "%s'.%s%c", output.context.before->string, output.context.after->string, f_string_eol_s[0]);
-      }
+      fll_error_print(print, status, function, fallback);
+      controller_entry_error_print_cache(print, cache);
+
+      f_thread_mutex_unlock(&thread->lock.print);
     }
   }
 #endif // _di_controller_entry_error_print_
 
+#ifndef _di_controller_entry_error_print_cache_
+  void controller_entry_error_print_cache(const fll_error_print_t print, const controller_cache_action_t cache) {
+
+    fprintf(print.to.stream, "%c", f_string_eol_s[0]);
+    fprintf(print.to.stream, "%s%sWhile processing ", print.context.before->string, print.prefix ? print.prefix : f_string_empty_s);
+
+    if (cache.name_action.used) {
+      fprintf(print.to.stream, "action '");
+      fprintf(print.to.stream, "%s%s%s%s", print.context.after->string, print.notable.before->string, cache.name_action.string, print.notable.after->string);
+      fprintf(print.to.stream, "%s' on line ", print.context.before->string);
+      fprintf(print.to.stream, "%s%s%llu%s", print.context.after->string, print.notable.before->string, cache.line_action, print.notable.after->string);
+      fprintf(print.to.stream, "%s for ", print.context.before->string);
+    }
+
+    if (cache.name_item.used) {
+      fprintf(print.to.stream, "entry item '");
+      fprintf(print.to.stream, "%s%s%s%s", print.context.after->string, print.notable.before->string, cache.name_item.string, print.notable.after->string);
+      fprintf(print.to.stream, "%s' on line ", print.context.before->string);
+      fprintf(print.to.stream, "%s%s%llu%s", print.context.after->string, print.notable.before->string, cache.line_item, print.notable.after->string);
+      fprintf(print.to.stream, "%s for ", print.context.before->string);
+    }
+
+    if (cache.name_file.used) {
+      fprintf(print.to.stream, "file '");
+      fprintf(print.to.stream, "%s%s%s%s", print.context.after->string, print.notable.before->string, cache.name_file.string, print.notable.after->string);
+      fprintf(print.to.stream, "%s'.%s%c", print.context.before->string, print.context.after->string, f_string_eol_s[0]);
+    }
+  }
+#endif // _di_controller_entry_error_print_cache_
+
 #ifndef _di_controller_entry_items_increase_by_
   f_status_t controller_entry_items_increase_by(const f_array_length_t amount, controller_entry_items_t *items) {
 
@@ -647,20 +659,24 @@ 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(thread_data.data->error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
+          controller_error_print(thread_data.data->error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true, thread_data);
         }
         else {
           status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
 
           if (F_status_is_error(status)) {
-            fll_error_print(thread_data.data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+            controller_error_print(thread_data.data->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, thread_data);
           }
         }
       }
       else {
         if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_lock(&thread_data.thread->lock.print);
+
           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]);
+
+          f_thread_unlock(&thread_data.thread->lock.print);
         }
 
         status = F_status_set_error(F_data_not);
@@ -740,12 +756,16 @@ extern "C" {
 
             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) {
+                f_thread_lock(&thread_data.thread->lock.print);
+
                 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(thread_data.data->warning, cache->action);
+                controller_entry_error_print_cache(thread_data.data->warning, cache->action);
+
+                f_thread_unlock(&thread_data.thread->lock.print);
               }
 
               code |= 0x2;
@@ -783,14 +803,18 @@ extern "C" {
           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(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+            controller_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data);
             break;
           }
 
           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(thread_data.data->error, cache->action);
+            f_thread_lock(&thread_data.thread->lock.print);
+
+            controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+            f_thread_unlock(&thread_data.thread->lock.print);
 
             if (F_status_set_fine(status) == F_memory_not) {
               break;
@@ -804,10 +828,14 @@ extern "C" {
 
           if (!(code & 0x1)) {
             if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+              f_thread_lock(&thread_data.thread->lock.print);
+
               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]);
+
+              f_thread_unlock(&thread_data.thread->lock.print);
             }
 
             status = F_status_set_error(F_found_not);
@@ -857,17 +885,21 @@ extern "C" {
                     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(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+                      controller_error_print(thread_data.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, thread_data);
                       break;
                     }
 
                     if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
+                      f_thread_lock(&thread_data.thread->lock.print);
+
                       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(thread_data.data->error, cache->action);
+                      controller_entry_error_print_cache(thread_data.data->error, cache->action);
+
+                      f_thread_unlock(&thread_data.thread->lock.print);
                     }
 
                     action->number = 0;
index 349aa760ab21133fbb858b4b7ad3ecf0ab0df42b..e4a8977c53b1cf248fb47fb3813c8e76b92a068e 100644 (file)
@@ -98,21 +98,48 @@ extern "C" {
 #endif // _di_controller_entry_actions_read_
 
 /**
- * Print additional error/warning information in addition to existing error.
+ * Print the entry related error, locking the print mutex during the print.
+ *
+ * @param print
+ *   Designates how printing is to be performed.
+ * @param cache
+ *   The action cache.
+ * @param status
+ *   The status code to process.
+ *   Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ *   The name of the function where the error happened.
+ *   Set to 0 to disable.
+ * @param fallback
+ *   Set to F_true to print the fallback error message for unknown errors.
+ * @param thread
+ *   The thread data.
+ *
+ * @see fll_error_print()
+ * @see controller_entry_error_print_cache()
+ */
+#ifndef _di_controller_entry_error_print_
+  extern void controller_entry_error_print(const fll_error_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_error_print_
+
+/**
+ * Print additional error/warning information in addition to existing error that is found within the cache.
  *
  * This is explicitly intended to be used in addition to the error message.
  *
- * @param output
- *   The error or warning output structure.
+ * This neither locks the thread nor does it check to see if output is enabled or disabled.
+ *
+ * @param print
+ *   Designates how printing is to be performed.
  * @param cache
- *   A structure for containing and caching relevant data.
+ *   The action cache.
  *
  * @see controller_entry_actions_read()
  * @see controller_entry_read()
  */
-#ifndef _di_controller_entry_error_print_
-  extern void controller_entry_error_print(const fll_error_print_t output, const controller_cache_action_t cache) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_entry_error_print_
+#ifndef _di_controller_entry_error_print_cache_
+  extern void controller_entry_error_print_cache(const fll_error_print_t print, const controller_cache_action_t cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_error_print_cache_
 
 /**
  * Increase the size of the entry items array by the specified amount, but only if necessary.
index a351cab943d0651f77ca86300566d10da3d6d2d0..8eb18ebdb91a3a1e1aca57248c05339797c31d50 100644 (file)
@@ -399,15 +399,118 @@ 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 controller_rule_copy(const controller_rule_t source, controller_rule_t *destination) {
     f_status_t status = F_none;
 
-    f_thread_condition_wait(&source->wait, &destination->lock);
+    destination->timeout_kill = source.timeout_kill;
+    destination->timeout_start = source.timeout_start;
+    destination->timeout_stop = source.timeout_stop;
 
-    // @todo
+    destination->has = source.has;
+    destination->nice = source.nice;
+    destination->user = source.user;
+    destination->group = source.group;
 
-    f_thread_condition_signal(&source->wait);
-    f_thread_mutex_unlock(&destination->lock);
+    destination->timestamp.seconds = source.seconds;
+    destination->timestamp.nanoseconds = source.nanoseconds;
+
+    destination->path_control.used = 0;
+    destination->name.used = 0;
+    destination->path.used = 0;
+    destination->script.used = 0;
+
+    destination->define.used = 0;
+    destination->parameter.used = 0;
+
+    destination->environment.used = 0;
+    destination->need.used = 0;
+    destination->want.used = 0;
+    destination->wish.used = 0;
+
+    destination->affinity.used = 0;
+    destination->capability.used = 0;
+    destination->control_group.used = 0;
+    destination->groups.used = 0;
+    destination->limits.used = 0;
+    destination->scheduler.used = 0;
+
+    destination->items.used = 0;
+
+    if (source.id.used) {
+
+      status = f_string_dynamic_append(source.id, &dynamic->id);
+      if (F_status_is_error(status)) return status;
+    }
+
+    if (source.name.used) {
+
+      status = f_string_dynamic_append(source.name, &dynamic->name);
+      if (F_status_is_error(status)) return status;
+    }
+
+    if (source.path.used) {
+
+      status = f_string_dynamic_append(source.path, &dynamic->path);
+      if (F_status_is_error(status)) return status;
+    }
+
+    if (source.script.used) {
+
+      status = f_string_dynamic_append(source.script, &dynamic->script);
+      if (F_status_is_error(status)) return status;
+    }
+
+    if (source.define.used) {
+      status = f_string_maps_append(source.define, &destination->define);
+    }
+
+    if (source.parameter.used) {
+      status = f_string_maps_append(source.parameter, &destination->parameter);
+    }
+
+    if (source.environment.used) {
+      status = f_string_dynamics_append(source.environment, &destination->environment);
+    }
+
+    if (source.need.used) {
+      status = f_string_dynamics_append(source.need, &destination->need);
+    }
+
+    if (source.want.used) {
+      status = f_string_dynamics_append(source.want, &destination->want);
+    }
+
+    if (source.wish.used) {
+      status = f_string_dynamics_append(source.wish, &destination->wish);
+    }
+
+    if (source.affinity.used) {
+      status = f_int32s_append(source.affinity, &destination->affinity);
+    }
+
+    if (source.capability.used) {
+      // @todo copy capability
+    }
+
+    if (source.control_group.used) {
+      // @todo copy control_group
+    }
+
+    if (source.groups.used) {
+      status = f_int32s_append(source.groups, &destination->groups);
+    }
+
+    if (source.limits.used) {
+      status = f_limit_sets_append(source.limits, &destination->limits);
+    }
+
+    if (source.scheduler.used) {
+      // @todo copy scheduler
+    }
+
+    if (source.items.used) {
+      // @todo copy items
+    }
 
     return status;
   }
@@ -452,11 +555,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->lock.print);
 
       controller_rule_error_print(output, cache, item);
 
-      f_thread_mutex_unlock(&thread->mutex.print);
+      f_thread_mutex_unlock(&thread->lock.print);
     }
   }
 #endif // _di_controller_rule_error_print_
@@ -705,14 +808,14 @@ extern "C" {
         else {
 
           if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
-            f_thread_mutex_lock(&thread_data.thread->mutex.print);
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
 
             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.data->warning, cache->action, F_true);
 
-            f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
 
           rule->items.array[i].actions.array[j].status = F_ignore;
@@ -760,7 +863,7 @@ extern "C" {
     if (options & controller_rule_option_simulate) {
 
       if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
-        f_thread_mutex_lock(&thread_data.thread->mutex.print);
+        f_thread_mutex_lock(&thread_data.thread->lock.print);
 
         fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
         fprintf(thread_data.data->output.stream, "Simulating execution of '");
@@ -775,7 +878,7 @@ extern "C" {
         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_data.thread->mutex.print);
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
       }
 
       // sleep for less than a second to better show simulation of synchronous vs asynchronous.
@@ -831,7 +934,7 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.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.data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
@@ -843,7 +946,7 @@ extern "C" {
         fll_error_print(thread_data.data->error, status, "fll_execute_program", F_true);
       }
 
-      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       status = F_status_set_error(status);
     }
@@ -869,7 +972,7 @@ extern "C" {
     if (options & controller_rule_option_simulate) {
 
       if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
-        f_thread_mutex_lock(&thread_data.thread->mutex.print);
+        f_thread_mutex_lock(&thread_data.thread->lock.print);
 
         fprintf(thread_data.data->output.stream, "%c", f_string_eol_s[0]);
         fprintf(thread_data.data->output.stream, "Simulating execution of '");
@@ -884,7 +987,7 @@ extern "C" {
         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_data.thread->mutex.print);
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
       }
 
       // sleep for less than a second to better show simulation of synchronous vs asynchronous.
@@ -940,7 +1043,7 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.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.data->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status);
@@ -952,7 +1055,7 @@ extern "C" {
         fll_error_print(thread_data.data->error, status, "fll_execute_program", F_true);
       }
 
-      f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return F_status_set_error(status);
     }
@@ -1381,7 +1484,9 @@ 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_data_t thread_data, controller_cache_t *cache) {
+  f_status_t controller_rule_process(const controller_rule_t rule, const f_array_length_t at_process, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) {
+
+    // @todo need to update accordingly with new parameters "rule" and "at_process".
 
     switch (action) {
       case controller_rule_action_type_freeze:
@@ -1398,7 +1503,7 @@ extern "C" {
       default:
 
         if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
-          f_thread_mutex_lock(&thread_data.thread->mutex.print);
+          f_thread_mutex_lock(&thread_data.thread->lock.print);
 
           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);
@@ -1407,19 +1512,19 @@ extern "C" {
 
           controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-          f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+          f_thread_mutex_unlock(&thread_data.thread->lock.print);
         }
 
         return F_status_set_error(F_parameter);
     }
 
     if (index >= thread_data.setting->rules.used) {
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return F_status_set_error(F_parameter);
     }
@@ -1429,12 +1534,12 @@ extern "C" {
     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_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return status;
     }
@@ -1445,7 +1550,7 @@ extern "C" {
 
       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);
+          f_thread_mutex_lock(&thread_data.thread->lock.print);
 
           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);
@@ -1454,7 +1559,7 @@ extern "C" {
 
           controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-          f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+          f_thread_mutex_unlock(&thread_data.thread->lock.print);
         }
 
         // never continue on recursion errors even in simulate mode.
@@ -1473,12 +1578,12 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return status;
     }
@@ -1486,12 +1591,12 @@ extern "C" {
     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_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return status;
     }
@@ -1503,12 +1608,12 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return status;
     }
@@ -1516,12 +1621,12 @@ extern "C" {
     status = f_string_dynamic_terminate_after(&cache->action.name_file);
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_lock(&thread_data.thread->mutex.print);
+      f_thread_mutex_lock(&thread_data.thread->lock.print);
 
       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_data.thread->mutex.print);
+      f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
       return status;
     }
@@ -1571,25 +1676,25 @@ extern "C" {
 
           if (at == thread_data.setting->rules.used) {
             if (i == 0) {
-              f_thread_mutex_lock(&thread_data.thread->mutex.print);
+              f_thread_mutex_lock(&thread_data.thread->lock.print);
 
               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.data->error, cache->action, F_true);
 
-              f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+              f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
               if (!(options & controller_rule_option_simulate)) break;
             }
             else {
               if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
-                f_thread_mutex_lock(&thread_data.thread->mutex.print);
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                 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_data.thread->mutex.print);
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
               }
             }
           }
@@ -1616,12 +1721,12 @@ extern "C" {
               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_data.thread->mutex.print);
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                 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_data.thread->mutex.print);
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
                 f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
                 f_thread_mutex_unlock(&thread_data.setting->rules.array[at].lock);
@@ -1676,12 +1781,12 @@ extern "C" {
               if (F_status_is_error(status)) {
                 if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not) {
 
-                  f_thread_mutex_lock(&thread_data.thread->mutex.print);
+                  f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                   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_data.thread->mutex.print);
+                  f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
                   if (!(options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) {
                     f_thread_condition_signal(&thread_data.setting->rules.array[at].wait);
@@ -1692,12 +1797,12 @@ extern "C" {
                 }
                 else {
                   if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
-                    f_thread_mutex_lock(&thread_data.thread->mutex.print);
+                    f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                     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_data.thread->mutex.print);
+                    f_thread_mutex_unlock(&thread_data.thread->lock.print);
                   }
                 }
               }
@@ -1709,25 +1814,25 @@ extern "C" {
             if (F_status_is_error(thread_data.setting->rules.array[at].status)) {
 
               if (i == 0 || i == 1) {
-                f_thread_mutex_lock(&thread_data.thread->mutex.print);
+                f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                 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.data->error, cache->action, F_true);
 
-                f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+                f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
                 if (!(options & controller_rule_option_simulate)) break;
               }
               else {
                 if (thread_data.data->warning.verbosity == f_console_verbosity_debug) {
-                  f_thread_mutex_lock(&thread_data.thread->mutex.print);
+                  f_thread_mutex_lock(&thread_data.thread->lock.print);
 
                   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_data.thread->mutex.print);
+                  f_thread_mutex_unlock(&thread_data.thread->lock.print);
                 }
               }
             }
@@ -1779,7 +1884,7 @@ extern "C" {
         if (missing) {
 
           if (thread_data.data->error.verbosity != f_console_verbosity_quiet) {
-            f_thread_mutex_lock(&thread_data.thread->mutex.print);
+            f_thread_mutex_lock(&thread_data.thread->lock.print);
 
             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);
@@ -1790,7 +1895,7 @@ extern "C" {
 
             controller_rule_error_print(thread_data.data->error, cache->action, F_true);
 
-            f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+            f_thread_mutex_unlock(&thread_data.thread->lock.print);
           }
 
           status = F_status_set_error(F_parameter);
@@ -1840,10 +1945,10 @@ extern "C" {
 #ifndef _di_controller_rule_process_asynchronous_
   f_status_t controller_rule_process_asynchronous(const f_array_length_t index, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) {
 
-    f_thread_mutex_lock(&thread_data->mutex.asynchronous);
+    f_thread_mutex_lock(&thread_data->lock.asynchronous);
 
     if (!thread_data.thread->enabled) {
-      f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
+      f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous);
 
       return F_signal;
     }
@@ -1851,7 +1956,7 @@ extern "C" {
     f_status_t status = controller_asynchronouss_increase(&thread_data.thread->asynchronouss);
 
     if (F_status_is_error(status)) {
-      f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
+      f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous);
 
       return status;
     }
@@ -1888,7 +1993,7 @@ extern "C" {
       thread_data.thread->asynchronouss.used--;
     }
 
-    f_thread_mutex_unlock(&thread_data.thread->mutex.asynchronous);
+    f_thread_mutex_unlock(&thread_data.thread->lock.asynchronous);
 
     if (F_status_is_error(status)) {
       return status;
@@ -3800,7 +3905,7 @@ extern "C" {
     controller_data_t *data = thread_data.thread->data;
     controller_setting_t *setting = thread_data.thread->setting;
 
-    f_thread_mutex_lock(&thread_data.thread->mutex.print);
+    f_thread_mutex_lock(&thread_data.thread->lock.print);
 
     switch (action) {
       case controller_rule_action_type_kill:
@@ -3821,7 +3926,7 @@ extern "C" {
           controller_rule_error_print(data->error, cache->action, F_true);
         }
 
-        f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+        f_thread_mutex_unlock(&thread_data.thread->lock.print);
 
         return;
     }
@@ -4097,7 +4202,7 @@ extern "C" {
 
     setting->rules.array[index].status = F_complete;
 
-    f_thread_mutex_unlock(&thread_data.thread->mutex.print);
+    f_thread_mutex_unlock(&thread_data.thread->lock.print);
   }
 #endif // _di_controller_rule_simulate_
 
@@ -4125,7 +4230,7 @@ extern "C" {
 
       if (!thread->enabled) break;
 
-      if (f_thread_mutex_lock_try(&thread->mutex.asynchronous) == F_none) {
+      if (f_thread_mutex_lock_try(&thread->lock.asynchronous) == F_none) {
 
         if (thread->asynchronouss.array[i].state != controller_asynchronous_state_joined) {
           f_thread_join(thread->asynchronouss.array[i].id, 0);
@@ -4139,7 +4244,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->lock.asynchronous);
       }
     } // for
   }
index cb26b9706948a5432e47871712c3cf5b2f931c74..12852ee1f7e6c44244aaed1b744989ab52cce94d 100644 (file)
@@ -132,6 +132,10 @@ extern "C" {
 /**
  * Copy a rule, allocating new space as necessary.
  *
+ * This does not do any locking or unlocking for the rule data, be sure to lock appropriately before and after calling this.
+ *
+ * @todo finish writing this.
+ *
  * @param source
  *   The source rule to copy from.
  * @param destination
@@ -370,6 +374,8 @@ extern "C" {
  * 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.
  *
+ * This does not do any locking or unlocking, be sure to lock appropriately before and after calling this.
+ *
  * @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.
@@ -533,6 +539,10 @@ 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 rule
+ *   The rule information at the time the rule process started.
+ * @param at_process
+ *   The position within the processs array representing this rule process.
  * @param action
  *   The action to perform based on the action type codes.
  *
@@ -558,7 +568,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_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_process(const controller_rule_t rule, const f_array_length_t at_process, const uint8_t action, const uint8_t options, controller_thread_data_t thread_data, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_process_
 
 /**
index 914f6b4f7b13f26dbfda3e07787888205c4abe0f..b6bb1599cc3e5459d39db2dbaa1ba3abb8093379 100644 (file)
@@ -27,7 +27,7 @@ extern "C" {
     thread.cache_main = thread_main->cache_main;
     thread.cache_action = &asynchronous->cache;
     thread.data = thread_main->data;
-    thread.mutex = thread_main->mutex;
+    thread.lock = thread_main->lock;
     thread.setting = thread_main->setting;
     thread.stack = &asynchronous->stack;
 
@@ -49,7 +49,7 @@ extern "C" {
 
     thread->enabled = F_false;
 
-    f_thread_mutex_lock(&thread->mutex.asynchronous);
+    f_thread_mutex_lock(&thread->lock.asynchronous);
 
     for (f_array_length_t i = 0; i < thread->asynchronouss.used; ++i) {
 
@@ -75,7 +75,7 @@ extern "C" {
 
     thread->asynchronouss.used = 0;
 
-    f_thread_mutex_unlock(&thread->mutex.asynchronous);
+    f_thread_mutex_unlock(&thread->lock.asynchronous);
   }
 #endif // _di_controller_thread_asynchronous_cancel_
 
@@ -91,7 +91,7 @@ extern "C" {
     for (; thread_data->thread->enabled; ) {
       sleep(interval);
 
-      if (f_thread_mutex_lock_try(&thread_data->thread->mutex.asynchronous) == F_none) {
+      if (f_thread_mutex_lock_try(&thread_data->thread->lock.asynchronous) == F_none) {
         controller_thread_t *thread = &thread_data->thread;
 
         if (thread->asynchronouss.used) {
@@ -146,7 +146,7 @@ extern "C" {
           controller_asynchronouss_resize(thread->asynchronouss.used, &thread->asynchronouss);
         }
 
-        f_thread_mutex_unlock(&thread->mutex.asynchronous);
+        f_thread_mutex_unlock(&thread->lock.asynchronous);
       }
     } // for
 
@@ -180,29 +180,26 @@ extern "C" {
       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 (data->error.verbosity != f_console_verbosity_quiet) {
-        controller_error_print_locked(data->error, F_status_set_fine(status), "f_thread_create", F_true, &thread);
+        controller_error_print(data->error, F_status_set_fine(status), "f_thread_create", F_true, &thread);
       }
     }
     else {
       if (data->parameters[controller_parameter_daemon].result == f_console_result_found) {
+
         setting->ready = controller_setting_ready_done;
 
         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);
+            f_thread_mutex_lock(&thread.lock.print);
 
             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.lock.print);
           }
 
           setting->ready = controller_setting_ready_abort;
@@ -225,14 +222,14 @@ extern "C" {
 
             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);
+                f_thread_mutex_lock(&thread.lock.print);
 
                 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.lock.print);
               }
 
               setting->ready = controller_setting_ready_fail;
@@ -278,6 +275,10 @@ extern "C" {
           status = f_thread_create(0, &thread.id_control, &controller_thread_control, (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 (data->error.verbosity != f_console_verbosity_quiet) {
             controller_error_print_locked(data->error, F_status_set_fine(status), "f_thread_create", F_true, &thread);
@@ -337,8 +338,8 @@ extern "C" {
     controller_thread_data_t *thread_data = (controller_thread_data_t *) arguments;
 
     // @todo
-    // f_thread_mutex_lock(&thread_data->mutex.rule);
-    // f_thread_mutex_unlock(&thread_data->mutex.rule);
+    // f_thread_mutex_lock(&thread_data->lock.rule);
+    // f_thread_mutex_unlock(&thread_data->lock.rule);
 
     return 0;
   }