]> Kevux Git Server - controller/commitdiff
Progress: Continue changes to locking.
authorKevin Day <Kevin@kevux.org>
Wed, 29 Oct 2025 11:24:07 +0000 (06:24 -0500)
committerKevin Day <Kevin@kevux.org>
Wed, 29 Oct 2025 11:40:19 +0000 (06:40 -0500)
Refactor `instance.lock` to `instance.use` to reduce the ambiguity regarding a lock named "lock".

Remove the `id` off of the instance.
This is not needed and removing it reduces the need for some locking.
The thread itself is now the "id" so `id_thread` is now renamed to just `thread`.
The `controller_instance_find()` function can now just get the instance pointer.

Add a "time" controller lock check flag.
Use this to designate expecting to return `F_time` on time outs.

Add additional documentation regarding locks.

Reduce uses of locking where possible by re-organizing code.
The `controller_instance_prepare()` function, in particular, has the the code changed to reduce how long the lock is held.
This is possible because the `controller_instance_find()` now gets the instance pointer rather than an identifier.
The `controller_instance_prepare()` function allocates the instance pointer independent of the array.
This allows for the write lock on instance to be only used when assigning to the array.
The instance must therefore be immediately freed on any error because it is not assigned to an array and cannot be auto-deallocated.

Add missing locks to areas where the locks should be, such as a read lock while reading the dependency rule status.

Make sure `instance.active` lock is set before calling `instance.use` locks (there may be more work needed regarding this).
Try to reduce how long `instance.use` are held (there may be more work needed regarding this).

24 files changed:
sources/c/program/controller/main/common/define.h
sources/c/program/controller/main/common/enumeration/instance.h
sources/c/program/controller/main/common/type/instance.c
sources/c/program/controller/main/common/type/instance.h
sources/c/program/controller/main/entry/process.c
sources/c/program/controller/main/instance.c
sources/c/program/controller/main/instance.h
sources/c/program/controller/main/instance/prepare.c
sources/c/program/controller/main/instance/prepare.h
sources/c/program/controller/main/instance/wait.c
sources/c/program/controller/main/instance/wait.h
sources/c/program/controller/main/lock.c
sources/c/program/controller/main/lock.h
sources/c/program/controller/main/mutex.c
sources/c/program/controller/main/mutex.h
sources/c/program/controller/main/print/error/lock.c
sources/c/program/controller/main/process.c
sources/c/program/controller/main/rule/execute.c
sources/c/program/controller/main/rule/execute.h
sources/c/program/controller/main/rule/instance.c
sources/c/program/controller/main/rule/instance.h
sources/c/program/controller/main/rule/wait.c
sources/c/program/controller/main/thread/cleanup.c
sources/c/program/controller/main/thread/instance.c

index 01c75fcc4d7deeeb2117b4d07fe34bd2e8a12180..01825f6f2fb29e1ac166d6b5b26851577d43b4a6 100644 (file)
@@ -42,12 +42,14 @@ extern "C" {
  * controller_lock_check_flag_*_d:
  *   - no:    Do not perform any checks; only return on success (or initial parameter errors before calling locks or waits).
  *   - yes:   Perform all checks and return (error check and enabled/disable check).
- *   - error: Only perform error check and return.
+ *   - error: Only perform error check and return error.
+ *   - time:  Perform error check and return error, otherwise if time out then return F_time.
  */
 #ifndef _di_controller_lock_check_flag_d_
   #define controller_lock_check_flag_no_d    0x0
   #define controller_lock_check_flag_yes_d   0x1
   #define controller_lock_check_flag_error_d 0x2
+  #define controller_lock_check_flag_time_d  0x4
 #endif // _di_controller_lock_check_flag_d_
 
 /**
index 2e247b38835acf96d0d955fe62c1752a8d9ffa72..c08710a15591f8114a20923194f42b49ce3f081f 100644 (file)
@@ -19,6 +19,8 @@ extern "C" {
 /**
  * Instance options.
  *
+ * @todo this should probably be defines rather than enums.
+ *
  * controller_instance_option_*_e:
  *   - asynchronous:      The Instance runs asynchronously.
  *   - require:           The Instance is required.
@@ -43,15 +45,15 @@ extern "C" {
  *
  * controller_instance_state_*_e:
  *   - idle:   No instance is running for this Rule.
- *   - busy:   A instance is actively using this, and is running synchronously.
  *   - active: A instance is actively using this, and is running asynchronously.
+ *   - busy:   A instance is actively using this, and is running synchronously.
  *   - done:   A instance has finished running on this and there is a thread that needs to be cleaned up.
  */
 #ifndef _di_controller_instance_state_e_
   enum {
     controller_instance_state_idle_e = 1,
-    controller_instance_state_busy_e,
     controller_instance_state_active_e,
+    controller_instance_state_busy_e,
     controller_instance_state_done_e,
   }; // enum
 #endif // _di_controller_instance_state_e_
index 7241ebb9d01568d756523dc5d3caeac7e119615c..c40c01a94cdc1628bdd901ed35c2bd13d1ca1bdd 100644 (file)
@@ -9,15 +9,15 @@ extern "C" {
 
     if (!instance) return;
 
-    if (instance->id_thread) {
-      f_thread_signal_write(instance->id_thread, F_signal_kill);
-      f_thread_join(instance->id_thread, 0);
+    if (instance->thread) {
+      f_thread_signal_write(instance->thread, F_signal_kill);
+      f_thread_join(instance->thread, 0);
 
-      instance->id_thread = 0;
+      instance->thread = 0;
     }
 
     f_thread_condition_delete(&instance->wait_condition);
-    f_thread_lock_delete(&instance->lock);
+    f_thread_lock_delete(&instance->use);
     f_thread_lock_delete(&instance->active);
     f_thread_mutex_full_delete(&instance->wait);
 
@@ -63,7 +63,7 @@ extern "C" {
     f_status_t status = f_memory_new(1, sizeof(controller_instance_t), (void **) instance);
 
     if (F_status_is_error_not(status)) {
-      status = f_thread_lock_create(0, &(*instance)->lock);
+      status = f_thread_lock_create(0, &(*instance)->use);
     }
 
     if (F_status_is_error_not(status)) {
index 8ace18b6af798db4e954c22615f637b6e8cfec95..27ec9b8e4a99bbacef8132537d9cdc5a40a6bda9 100644 (file)
@@ -26,37 +26,36 @@ extern "C" {
  *
  * The typedef for this is located in the defs.h header.
  *
- * The "active" lock is used for asynchronous operations.
- * This can be used to check if anything is asynchronously operating the rule.
+ * The "active" lock is used for asynchronous execution of a rule.
+ * This can be used to check if anything is asynchronously operating the rule and designates that something is actively using the instance.
+ * A write lock on "active" should be used to prevent activation.
  *
  * Properties:
- *   - id:        The ID of this process relative to the processes array.
- *   - id_thread: The thread ID, a valid ID when state is "active", and an invalid ID when the state is "busy".
+ *   - thread: The thread ID, a valid ID when state is "active", and an invalid ID when the state is "busy".
  *
- *   - action:  The Action being performed.
- *   - options: Configuration options for this thread.
+ *   - action:  The Action being performed (should not need locking to read or write to).
+ *   - options: Configuration options for this thread (should not need locking to read or write to).
  *   - state:   The state of the process.
- *   - type:    The currently active process type (from the controller_instance_type_*_e).
+ *   - type:    The currently active process type (from the controller_instance_type_*_e) (should not need locking to read or write to).
  *   - result:  The last return code from an execution of a process.
  *
  *   - active:         A read/write lock representing that something is currently using this (read locks = in use, write lock = begin deleting).
- *   - lock:           A read/write lock on the structure.
+ *   - use:            A read/write lock on the structure, except for specially noted properties.
  *   - wait:           A mutex lock for working with "wait".
  *   - wait_condition: A thread condition to tell a process waiting process that the Rule has is done being processed.
  *
  *   - child:       The process id of a child process, if one is running (when forking to execute a child process).
  *   - path_pids:   An array of paths representing PID files.
- *   - environment: Environment data used when executing the instance.
+ *   - environment: Environment data used when executing the instance (should not need locking to read or write to).
  *   - stack:       A stack used to represent dependencies as Rule ID's to avoid circular Rule dependencies (If Rule A waits on Rule B, then Rule B must not wait on Rule A).
  *
- *   - rule:  A copy of the Rule actively being executed.
- *   - cache: The cache used by this Instance.
+ *   - rule:  A copy of the Rule actively being executed (should not need locking to read or write to).
+ *   - cache: The cache used by this Instance (should not need locking to read or write to).
  *   - main:  The main program data.
  */
 #ifndef _di_controller_instance_t_
   struct controller_instance_t_ {
-    f_number_unsigned_t id;
-    f_thread_id_t id_thread;
+    f_thread_id_t thread;
 
     uint8_t action;
     uint8_t options;
@@ -65,7 +64,7 @@ extern "C" {
     int result;
 
     f_thread_lock_t active;
-    f_thread_lock_t lock;
+    f_thread_lock_t use;
     f_thread_mutex_full_t wait;
     f_thread_condition_t wait_condition;
 
@@ -80,7 +79,6 @@ extern "C" {
   };
 
   #define controller_instance_t_initialize { \
-    0, \
     f_thread_id_t_initialize, \
     0, \
     0, \
index 9e146e21eae0e1abf46172a90b78960206bd5f90..e1b7fb1d27c2821c4007ebf2c82639da98538174 100644 (file)
@@ -275,7 +275,9 @@ extern "C" {
             cache->action.line_item = cache_line_item;
 
             if (status_lock != F_okay) {
-              controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+              if (F_status_set_fine(status_lock) != F_interrupt) {
+                controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+              }
 
               break;
             }
index 7ad9a912a9ef47f5ab35bf58fe0a2f84ea98012d..afb80dce6e0707113d140cd437ca970f7317c1a7 100644 (file)
@@ -5,7 +5,7 @@ extern "C" {
 #endif
 
 #ifndef _di_controller_instance_find_
-  f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, f_number_unsigned_t * const at) {
+  f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, controller_instance_t ** const instance) {
 
     if (!alias.used) return F_okay;
     if (!instances.used) return F_false;
@@ -13,7 +13,7 @@ extern "C" {
     for (f_number_unsigned_t i = 0; i < instances.used; ++i) {
 
       if (instances.array[i] && instances.array[i]->action == action && f_compare_dynamic(alias, instances.array[i]->rule.alias) == F_equal_to) {
-        if (at) *at = i;
+        *instance = instances.array[i];
 
         return F_true;
       }
index 44a5acb0c96d6f4cb26268976f35c1fb30ce2165..1e0e00ddaaf616350e6110f75bd065d1f85d6bd5 100644 (file)
@@ -27,10 +27,10 @@ extern "C" {
  *   The Rule alias to find.
  * @param instances
  *   The array of instancees to.
- * @param at
- *   (optional) The location within instances the id was found.
+ * @param instance
+ *   The found instance.
  *
- *   Set to NULL to disable.
+ *   Must not be NULL.
  *
  * @return
  *   F_okay if not given a valid id to search.
@@ -38,7 +38,7 @@ extern "C" {
  *   F_true if there is a instance found (address is stored in "at").
  */
 #ifndef _di_controller_instance_find_
-  f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, f_number_unsigned_t * const at);
+  f_status_t controller_instance_find(const f_number_unsigned_t action, const f_string_static_t alias, const controller_instances_t instances, controller_instance_t ** const instance);
 #endif // _di_controller_instance_find_
 
 #ifdef __cplusplus
index ceb9f932e2315663588128ead923f785302835bb..4cdff54aea38a1b4ee2ed37cd444c0cc2626901c 100644 (file)
@@ -5,76 +5,61 @@ extern "C" {
 #endif
 
 #ifndef _di_controller_instance_prepare_
-  f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t * const id) {
+  f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance) {
 
     if (!main) return F_status_set_error(F_parameter);
 
-    f_status_t status = F_okay;
+    if (!controller_thread_enable_is_normal(&main->thread, is_normal)) return F_status_set_error(F_interrupt);
 
-    if (controller_instance_find(action, alias, main->thread.instances, id) == F_false) {
-      f_thread_unlock(&main->thread.lock.instance);
+    f_status_t status = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
 
-      status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
-
-      if (status != F_okay) {
-        controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
-      }
-      else {
-        status = f_memory_array_increase(controller_allocation_small_d, sizeof(controller_instance_t), (void **) &main->thread.instances.array, &main->thread.instances.used, &main->thread.instances.size);
+    if (status != F_okay) {
+      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
 
-        // The Instances array has instance as a double-pointer.
-        if (F_status_is_error_not(status) && !main->thread.instances.array[main->thread.instances.used]) {
-          status = controller_instance_initialize(&main->thread.instances.array[main->thread.instances.used]);
-        }
+      return status;
+    }
 
-        if (F_status_is_error_not(status)) {
+    status = controller_instance_find(action, alias, main->thread.instances, instance);
 
-          // The Instances array has instance as a double-pointer.
-          status = controller_instance_initialize(&main->thread.instances.array[main->thread.instances.used]);
+    f_thread_unlock(&main->thread.lock.instance);
 
-          controller_instance_t * const instance = F_status_is_error_not(status) ? main->thread.instances.array[main->thread.instances.used] : 0;
+    if (status == F_true) return F_found;
+    if (status == F_okay) return F_found_not;
 
-          if (status == F_okay) {
-            status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance->lock);
-          }
+    // Instantiate the instance pointer off of the instances array to avoid needing to lock anything on the new instance.
+    status = controller_instance_initialize(instance);
+    if (F_status_is_error(status)) return status;
 
-          if (F_status_is_error(status)) {
-            controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
-          }
-          else {
-            instance->action = action;
-            instance->rule.alias.used = 0;
-            instance->main = main;
+    (*instance)->action = action;
+    (*instance)->rule.alias.used = 0;
+    (*instance)->main = main;
 
-            status = f_string_dynamic_append(alias, &instance->rule.alias);
+    status = f_string_dynamic_append(alias, &(*instance)->rule.alias);
 
-            if (F_status_is_error_not(status)) {
-              instance->id = main->thread.instances.used++;
-              status = F_okay;
+    if (F_status_is_error_not(status)) {
+      status = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
 
-              if (id) {
-                *id = instance->id;
-              }
-            }
+      if (F_status_is_error(status)) {
+        controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
+      }
+      else {
+        if (F_status_is_error_not(status)) {
+          status = f_memory_array_increase(controller_allocation_small_d, sizeof(controller_instance_t), (void **) &main->thread.instances.array, &main->thread.instances.used, &main->thread.instances.size);
 
-            f_thread_unlock(&instance->lock);
+          // The Instances array has instance as a double-pointer.
+          if (F_status_is_error_not(status)) {
+            main->thread.instances.array[main->thread.instances.used++] = *instance;
           }
         }
 
         f_thread_unlock(&main->thread.lock.instance);
       }
-
-      // The read lock must be restored on return.
-      const f_status_t status_restore = F_status_is_error(controller_lock_read_standard(is_normal, controller_lock_check_flag_no_d, &main->thread, &main->thread.lock.instance))
-        ? F_status_set_error(F_lock_read)
-        : F_okay;
-
-      if (F_status_is_fine(status)) {
-        status = status_restore;
-      }
     }
-    else {
-      status = F_found;
+
+    // De-allocate instance on error because it is not yet assigned to the array.
+    if (F_status_is_error(status)) {
+      controller_instance_delete(*instance);
+      *instance = 0;
     }
 
     return status;
@@ -82,9 +67,9 @@ extern "C" {
 #endif // _di_controller_instance_prepare_
 
 #ifndef _di_controller_instance_prepare_instance_type_
-  f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t *id) {
+  f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance) {
 
-    return controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias, id);
+    return controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias, instance);
   }
 #endif // _di_controller_instance_prepare_instance_type_
 
index 0af2e85e0201cd72e550e3223c6978f77368f3b8..9200362ae641fafa155a6472ef8591d5f2f982cb 100644 (file)
@@ -24,8 +24,6 @@ extern "C" {
  *
  * If a instance by the given Rule alias and Rule Action already exists, then nothing is done.
  *
- * This requires that a main.thread.lock.instance lock be set on instance.lock before being called.
- *
  * @param main
  *   The main program data.
  *
@@ -37,29 +35,28 @@ extern "C" {
  *   The Rule Action to use.
  * @param alias
  *   The Rule alias to use.
- * @param id
- *   (optional) The instance ID when found or created.
+ * @param instance
+ *   The memory address of the prepared instance is assigned to this on success.
  *
- *   Set to NULL to not use.
+ *   Must not be NULL.
  *
  * @return
  *   F_okay on success.
- *   F_found on success, but nothing was done because an existing instance was found.
+ *   F_found on an existing instance being found.
+ *   F_found_not no instance found because there is no alias to find.
  *
- *   F_lock_read (with error bit) if failed to re-establish read lock on main.thread.lock.instance while returning.
  *   F_parameter (with error bit) if a parameter is invalid.
  *
  *   Errors (with error bit) from: f_string_dynamic_append().
  *
- *   Errors (with error bit) from: controller_lock_read().
- *   Errors (with error bit) from: controller_lock_write().
+ *   Errors (with error bit) from: controller_lock_read_standard().
+ *   Errors (with error bit) from: controller_lock_write_standard().
  *
  * @see f_string_dynamic_append()
- * @see controller_lock_read()
- * @see controller_lock_write()
+ * @see controller_lock_write_standard()
  */
 #ifndef _di_controller_instance_prepare_
-  extern f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t *id);
+  extern f_status_t controller_instance_prepare(controller_t * const main, const uint8_t is_normal, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance);
 #endif // _di_controller_instance_prepare_
 
 /**
@@ -70,8 +67,6 @@ extern "C" {
  *
  * If a instance by the given Rule alias and Rule Action already exists, then nothing is done.
  *
- * This requires that a main.thread.lock.instance lock be set on instance->lock before being called.
- *
  * @param main
  *   The main program data.
  *
@@ -82,10 +77,10 @@ extern "C" {
  *   The Rule Action to use.
  * @param alias
  *   The Rule alias to use.
- * @param id
- *   (optional) The instance ID when found or created.
+ * @param instance
+ *   The memory address of the prepared instance is assigned to this on success.
  *
- *   Set to NULL to not use.
+ *   Must not be NULL.
  *
  * @return
  *   Success from: controller_instance_prepare()
@@ -95,7 +90,7 @@ extern "C" {
  * @see controller_instance_prepare()
  */
 #ifndef _di_controller_instance_prepare_instance_type_
-  extern f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_number_unsigned_t * const id);
+  extern f_status_t controller_instance_prepare_instance_type(controller_t * const main, const uint8_t type, const uint8_t action, const f_string_static_t alias, controller_instance_t ** const instance);
 #endif // _di_controller_instance_prepare_instance_type_
 
 #ifdef __cplusplus
index d35c50ac27c7484538d417ea84c8ec860d65452c..2bbe898a660ed9afc80bbbf3e1049c6cddf2f8cd 100644 (file)
@@ -19,9 +19,14 @@ extern "C" {
 
     do {
       status = controller_mutex_lock_instance(instance, &instance->wait);
-      if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
       if (F_status_is_error(status)) break;
 
+      if (!controller_thread_is_enabled_instance(instance)) {
+        f_thread_mutex_unlock(&instance->wait.mutex);
+
+        return F_status_set_error(F_interrupt);
+      }
+
       if (count < controller_thread_timeout_wait_1_before_d) {
         controller_time_now(controller_thread_timeout_wait_1_seconds_d, controller_thread_timeout_wait_1_nanoseconds_d, &time);
       }
@@ -42,7 +47,7 @@ extern "C" {
       if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
       if (F_status_is_error(status)) break;
 
-      status_lock = controller_lock_read_instance(instance, &instance->lock);
+      status_lock = controller_lock_read_instance(instance, &instance->use);
 
       if (F_status_is_error(status_lock)) {
         controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status_lock), F_true);
@@ -51,11 +56,13 @@ extern "C" {
       }
 
       if (!controller_rule_status_is_available(instance->action, instance->rule) && !(instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) {
-        f_thread_unlock(&instance->lock);
+        f_thread_unlock(&instance->use);
 
         return F_okay;
       }
 
+      f_thread_unlock(&instance->use);
+
       if (status != F_time) {
 
         // Move up the wait timer after a trigger was received.
@@ -70,8 +77,6 @@ extern "C" {
         }
       }
 
-      f_thread_unlock(&instance->lock);
-
       if (count < controller_thread_timeout_wait_3_before_d) {
         ++count;
       }
index 8717a04d9505b6f1d685c956766fac061ed26a98..c050fbc2e6d10b6751bd4fe3fc526ad190c11da2 100644 (file)
@@ -22,6 +22,9 @@ extern "C" {
  * @param instance
  *   The instance to wait on.
  *
+ *   The instance.active lock must be set before calling this.
+ *   The instance.use lock must not be set before calling this.
+ *
  *   Must not be NULL.
  *
  * @return
index cb98750e6ebc2358533dc88bb6f6d2d145d1ac8e..e1a79cb80af451df9467327f16a6df9ddb9bbe1b 100644 (file)
@@ -115,8 +115,9 @@ extern "C" {
 
       if (status == F_time) {
         if (check) {
-          if (check == controller_lock_check_flag_error_d) {
+          if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
             if (F_status_is_error(status)) return status;
+            if (check == controller_lock_check_flag_time_d) return F_time;
           }
           else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
         }
@@ -161,8 +162,9 @@ extern "C" {
 
       if (status == F_time) {
         if (check) {
-          if (check == controller_lock_check_flag_error_d) {
+          if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
             if (F_status_is_error(status)) return status;
+            if (check == controller_lock_check_flag_time_d) return F_time;
           }
           else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
         }
index 5ce54481c996fb5976b2e3b8dfac48f2d9e08da5..0f5927398c605a36c244252164d0b52b5026007e 100644 (file)
@@ -53,6 +53,7 @@ extern "C" {
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  * @param seconds
  *   The seconds to add to current time.
  * @param nanoseconds
@@ -94,6 +95,7 @@ extern "C" {
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  * @param thread
  *   The thread data used to determine if the main thread is disabled or not.
  *
@@ -153,6 +155,7 @@ extern "C" {
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  * @param seconds
  *   The seconds to add to current time.
  * @param nanoseconds
@@ -193,6 +196,7 @@ extern "C" {
  * @param check
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
  * @param thread
  *   The thread data used to determine if the main thread is disabled or not.
index 89e2cf5f415be839417871aea7d010f0a8ac5639..b93a6facdd726249390d8f4d5b529fdc1d5c6593 100644 (file)
@@ -70,8 +70,9 @@ extern "C" {
 
       if (status == F_time) {
         if (check) {
-          if (check == controller_lock_check_flag_error_d) {
+          if (check == controller_lock_check_flag_error_d || check == controller_lock_check_flag_time_d) {
             if (F_status_is_error(status)) return status;
+            if (check == controller_lock_check_flag_time_d) return F_time;
           }
           else if (!controller_thread_enable_is_normal(thread, is_normal)) return F_status_set_error(F_interrupt);
         }
index 7fe517445ce143f11b0ecd3bcbd8dd80f47520ae..b0389dc1b30c551f9e96dad232a0279b6114491c 100644 (file)
@@ -92,12 +92,13 @@ extern "C" {
  * Given a mutex lock, periodically check to see if main thread is disabled while waiting.
  *
  * @param is_normal
+ *   If TRUE, then perform as if this operates during a normal operation (Entry and Control).
+ *   If FALSE, then perform as if this operates during a an Exit operation.
+ * @param check
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
- * @param check
- *   If TRUE, then check if the state is enabled and if it is not then abort.
- *   If FALSE, then do not check if the state is enabled and keep looping.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  * @param seconds
  *   The seconds to add to current time.
  * @param nanoseconds
@@ -113,6 +114,7 @@ extern "C" {
  *
  * @return
  *   F_okay on success.
+ *   F_time on out of time, but only if check is controller_lock_check_flag_time_d.
  *
  *   F_interrupt (with error bit set) on (exit) signal received, lock will not be set when this is returned.
  *   F_parameter (with error bit) if a parameter is invalid.
@@ -133,12 +135,13 @@ extern "C" {
  * Given a mutex lock, periodically check to see if main thread is disabled while waiting.
  *
  * @param is_normal
+ *   If TRUE, then perform as if this operates during a normal operation (Entry and Control).
+ *   If FALSE, then perform as if this operates during a an Exit operation.
+ * @param check
  *   IF controller_lock_check_flag_error_d, then this is is also TRUE, but it does not call controller_thread_enable_is_normal() and returns on any lock error.
  *   If controller_lock_check_flag_no_d, then do not check if the state is enabled and keep looping.
  *   If controller_lock_check_flag_yes_d, then check if the state is enabled and if it is not then abort.
- * @param check
- *   If TRUE, then check if the state is enabled and if it is not then abort.
- *   If FALSE, then do not check if the state is enabled and keep looping.
+ *   If controller_lock_check_flag_time_d, then identical to controller_lock_check_flag_error_d but returns F_time on time out.
  * @param thread
  *   The thread data used to determine if the main thread is disabled or not.
  *
index 80c4587b7d419fdd238144e4529852631b45ca33..298bea8393604469f6523cde58e49e278ad619b6 100644 (file)
@@ -14,12 +14,11 @@ extern "C" {
 
     controller_lock_print(print->to, &main->thread);
 
-    fl_print_format("%r%[%QThe pid file '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
-    fl_print_format("%['Critical failure while attempting to establish '%]", print->to, print->context, print->context);
+    fl_print_format("%r%[Critical failure while attempting to establish '%]", print->to, f_string_eol_s, print->context, print->context);
     fl_print_format("%[%r lock%]", print->to, print->notable, is_read ? f_file_operation_read_s : f_file_operation_write_s, print->notable);
 
     if (status != F_failure) {
-      fl_print_format(" %['due to%] ", print->to, print->context, print->context);
+      fl_print_format("%[' due to '%]", print->to, print->context, print->context);
 
       if (status == F_parameter) {
         fl_print_format("%[Invalid Parameter%]", print->to, print->notable, print->notable);
index 35b646d1bd4a6d17c9fe061b417bdc806e254344..1e707688e139bd3ab03c69db21240857a6fbea0b 100644 (file)
@@ -90,10 +90,11 @@ extern "C" {
             controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
           }
           else {
-            controller_thread_join(&main->thread.id_entry);
+            if (main->thread.id_entry) {
+              controller_thread_join(&main->thread.id_entry);
+            }
 
             status = main->thread.status;
-            main->thread.id_entry = 0;
           }
         }
       }
@@ -104,16 +105,16 @@ extern "C" {
       if (!(main->setting.flag & controller_main_flag_validate_d)) {
 
         // Wait for the Entry thread to complete before starting the Rule thread.
-        controller_thread_join(&main->thread.id_rule);
+        if (main->thread.id_rule) {
+          controller_thread_join(&main->thread.id_rule);
+        }
 
         if (controller_thread_enable_get(&main->thread) > controller_thread_enable_not_e && main->process.mode == controller_process_mode_service_e) {
           status = f_thread_create(0, &main->thread.id_rule, &controller_thread_rule, (void *) main);
 
           if (F_status_is_error(status)) {
             main->thread.id_rule = 0;
-          }
 
-          if (F_status_is_error(status)) {
             controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
           }
         }
@@ -128,7 +129,9 @@ extern "C" {
 
     if (F_status_is_error_not(status) && status != F_failure && !(main->setting.flag & controller_main_flag_validate_d) && controller_thread_enable_is_normal(&main->thread, F_true)) {
       if (main->process.mode == controller_process_mode_service_e) {
-        controller_thread_join(&main->thread.id_signal);
+        if (main->thread.id_signal) {
+          controller_thread_join(&main->thread.id_signal);
+        }
       }
       else if (main->process.mode == controller_process_mode_helper_e || main->process.mode == controller_process_mode_program_e) {
         status = controller_rule_wait_all(main, F_true, F_false);
index 2eb5ebe0d2c2dd15c76759b48ad08c067cb7c494..d7da15faf7bdf5683b2ae1b3895dc59245f3b4e8 100644 (file)
@@ -406,15 +406,10 @@ extern "C" {
     } // for
 
     // Lock failed, attempt to re-establish lock before returning.
-    if (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read) {
+    if (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read || F_status_set_fine(status) == F_lock_write) {
       success = F_false;
 
-      if (F_status_set_fine(status) == F_lock_read) {
-        status = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
-        if (status != F_okay) return F_status_set_error(F_lock_read);
-
-        status = F_status_set_error(F_lock);
-      }
+      return status;
     }
 
     if (success == false && !instance->rule.items.used) {
@@ -439,19 +434,22 @@ extern "C" {
     f_status_t status = F_okay;
     f_execute_result_t result = f_execute_result_t_initialize;
 
-    // @fixme Lock the instance.childs.used with write access. (active should already have a read lock)
-    status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
+    status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->use);
     if (status != F_okay) return F_status_set_error(F_lock_read);
 
     status = f_memory_array_increase(controller_allocation_small_d, sizeof(pid_t), (void **) &instance->childs.array, &instance->childs.used, &instance->childs.size);
 
     if (F_status_is_error(status)) {
+      if (F_status_set_fine(status) == F_interrupt) return status;
+
       controller_print_error_status(&main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
 
       return status;
     }
 
-    pid_t * const process_child_id = controller_rule_execute_next_child(instance); // @fixme should be included in write lock.
+    pid_t * const process_child_id = controller_rule_execute_next_child(instance);
+
+    f_thread_unlock(&instance->use);
 
     if (options & controller_instance_option_simulate_e) {
       controller_print_entry_output_execute_simulate(&main->program.output, instance, program, arguments);
@@ -484,17 +482,12 @@ extern "C" {
       // Have the parent wait for the child instance to finish.
       waitpid(id_child, &result.status, 0);
 
-      f_thread_unlock(&instance->lock);
-
-      f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
+      f_status_t status_lock = controller_lock_write_instance(instance, &instance->use);
 
       if (F_status_is_error(status_lock)) {
         controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
 
-        status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
-        if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
-        return F_status_set_error(F_lock);
+        return status_lock;
       }
 
       instance->result = result.status;
@@ -502,23 +495,7 @@ extern "C" {
       // Remove the pid now that waidpid() has returned.
       *process_child_id = 0;
 
-      f_thread_unlock(&instance->lock);
-
-      status_lock = controller_lock_read_instance(instance, &instance->lock);
-
-      if (status_lock != F_okay) {
-
-        // Try again, after the first interrupt.
-        if (F_status_set_fine(status_lock) == F_interrupt) {
-          status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
-        }
-
-        if (status_lock != F_okay) {
-          controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
-          return F_status_set_error(F_lock_read);
-        }
-      }
+      f_thread_unlock(&instance->use);
 
       status = WIFEXITED(result.status) && WEXITSTATUS(result.status) ? F_status_set_error(F_failure) : F_okay;
     }
@@ -544,6 +521,16 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
+      {
+        const f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
+
+        if (F_status_is_error(status_lock)) {
+          controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+          return status_lock;
+        }
+      }
+
       if ((WIFEXITED(instance->result) && WEXITSTATUS(instance->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
         controller_print_error_rule_item_execute(&instance->main->program.error, type == controller_rule_item_type_script_e, program.used ? program : arguments.array[0], status, instance->result);
       }
@@ -551,6 +538,8 @@ extern "C" {
         controller_print_error_status(&instance->main->program.error, macro_controller_f(fll_execute_program), F_status_set_fine(status));
       }
 
+      f_thread_unlock(&instance->use);
+
       status = F_status_set_error(status);
     }
 
@@ -605,9 +594,8 @@ extern "C" {
     f_status_t status = F_okay;
     f_execute_result_t result = f_execute_result_t_initialize;
 
-    // @fixme Lock the instance.childs.used with write access. (active should already have a read lock)
-    status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
-    if (status != F_okay) return F_status_set_error(F_lock_read);
+    status = controller_lock_write_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->use);
+    if (F_status_is_error(status)) return status;
 
     status = f_memory_array_increase(controller_allocation_small_d, sizeof(pid_t), (void **) &instance->childs.array, &instance->childs.used, &instance->childs.size);
 
@@ -616,14 +604,18 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
+      f_thread_unlock(&instance->use);
+
       controller_print_error_status(&main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
 
       return status;
     }
 
-    pid_t * const process_child_id = controller_rule_execute_next_child(instance); // @fixme should be included in write lock.
+    pid_t * const process_child_id = controller_rule_execute_next_child(instance);
     f_string_dynamic_t * const process_child_pid_file = controller_rule_execute_next_pid_path(instance);
 
+    f_thread_unlock(&instance->use);
+
     status = f_file_exists(pid_file, F_true);
 
     if (F_status_is_error(status) || status == F_true) {
@@ -678,42 +670,28 @@ extern "C" {
       // Have the parent wait for the child instance to finish.
       waitpid(id_child, &result.status, 0);
 
-      f_thread_unlock(&instance->lock);
-
-      f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
+      f_status_t status_lock = controller_lock_write_instance(instance, &instance->use);
 
       if (status_lock != F_okay) {
         controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
 
-        status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
-        if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
-        return F_status_set_error(F_lock);
+        return status_lock;
       }
 
       // Assign the child instance id to allow for the cancel instance to send appropriate termination signals to the child instance.
       *process_child_id = id_child;
 
-      // The child instance should perform the change into background, therefore it is safe to wait for the child to Exit (another instance is spawned).
-      if (F_status_set_fine(status_lock) != F_interrupt) {
-        waitpid(id_child, &result.status, 0);
-      }
-
-      if (!controller_thread_is_enabled_instance(instance)) return status_lock == F_okay ? F_status_set_error(F_interrupt) : F_status_set_error(F_lock);
+      f_thread_unlock(&instance->use);
 
-      if (status_lock == F_okay) {
-        f_thread_unlock(&instance->lock);
-      }
+      // The child instance should perform the change into background, therefore it is safe to wait for the child to Exit (another instance is spawned).
+      waitpid(id_child, &result.status, 0);
 
-      status_lock = controller_lock_write_instance(instance, &instance->lock);
+      status_lock = controller_lock_write_instance(instance, &instance->use);
 
       if (status_lock != F_okay) {
         controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
 
-        status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &instance->main->thread, &instance->lock);
-        if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
-        return F_status_set_error(F_lock);
+        return F_status_set_error(F_lock_write);
       }
 
       instance->result = result.status;
@@ -721,24 +699,16 @@ extern "C" {
       // Remove the pid now that waidpid() has returned.
       *process_child_id = 0;
 
-      f_thread_unlock(&instance->lock);
-
-      status_lock = controller_lock_read_instance(instance, &instance->lock);
-
-      if (F_status_is_error(status_lock)) {
-        controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
-        return F_status_set_error(F_lock);
-      }
+      f_thread_unlock(&instance->use);
 
       status = WIFEXITED(result.status) && WEXITSTATUS(result.status) ? F_status_set_error(F_failure) : F_okay;
     }
     else {
       main->program.child = result.status;
-
-      if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
     }
 
+    if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
+
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
@@ -755,6 +725,16 @@ extern "C" {
     if (F_status_is_error(status)) {
       status = F_status_set_fine(status);
 
+      {
+        const f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
+
+        if (F_status_is_error(status_lock)) {
+          controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+          return status_lock;
+        }
+      }
+
       if ((WIFEXITED(instance->result) && WEXITSTATUS(instance->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
         controller_print_error_rule_item_execute(&instance->main->program.error, type == controller_rule_item_type_utility_e, program.used ? program : arguments.array[0], status, instance->result);
       }
@@ -762,6 +742,8 @@ extern "C" {
         controller_print_error_status(&instance->main->program.error, macro_controller_f(fll_execute_program), F_status_set_fine(status));
       }
 
+      f_thread_unlock(&instance->use);
+
       return F_status_set_error(status);
     }
 
index afb25702f893b3b3025dcb391100dfa12e85bc1e..c1413df3e4ac483c3f7fe35d4ebc88ae08ad5c57 100644 (file)
@@ -19,7 +19,7 @@ extern "C" {
 /**
  * Perform an execution of the given Rule.
  *
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
  *
  * @param main
  *   The main program data.
@@ -53,8 +53,8 @@ extern "C" {
  *
  *   F_failure (with error bit) if failed to execute.
  *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- *   F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- *   F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ *   F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ *   F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
  *
  *   On success and the Rule is run synchronously, then the individual status for the Rule is set to F_complete.
  *   On success and the Rule is run asynchronously, then the individual status for the Rule is set to F_busy.
@@ -67,7 +67,7 @@ extern "C" {
 /**
  * Perform an execution of the given Rule in the foreground.
  *
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
  *
  * @param instance
  *   The instance data.
@@ -92,8 +92,8 @@ extern "C" {
  *   F_child on child process exiting.
  *
  *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- *   F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- *   F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ *   F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ *   F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
  *
  *   Errors (with error bit) from: fll_execute_program().
  *
@@ -106,6 +106,9 @@ extern "C" {
 /**
  * Find the next available child slot and return a pointer to that location.
  *
+ * This does not lock the instance.use lock and does access instance.childs.
+ * The caller must ensure the instance.use is write locked before calling this.
+ *
  * @param instance
  *   The instance data.
  *
@@ -144,7 +147,7 @@ extern "C" {
 /**
  * Perform an execution of the given Rule in the foreground or background and creating a PID file.
  *
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
  *
  * When this is synchronous, this will wait for the PID file to be generated before continuing.
  * When this is asynchronous, this will continue on adding the Rule id and Action to the asynchronous list.
@@ -177,8 +180,8 @@ extern "C" {
  *
  *   F_file_found (with error bit) if the PID file already exists.
  *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
- *   F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- *   F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ *   F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ *   F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
  *
  *   Errors (with error bit) from: fll_execute_program().
  *
index d0152298c6ec09633da694f9b7d49316f14df6dd..452e0f6f6cef9010f0ada47f2ae44245056ccb72 100644 (file)
@@ -94,7 +94,6 @@ extern "C" {
     {
       f_number_unsigned_t j = 0;
       f_number_unsigned_t id_rule = 0;
-      f_number_unsigned_t id_dependency = 0;
 
       controller_instance_t *dependency = 0;
 
@@ -129,56 +128,43 @@ extern "C" {
 
         for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_instance(instance); ++j) {
 
-          id_dependency = 0;
           dependency = 0;
           found = F_false;
 
-          status_lock = controller_lock_read_instance(instance, &main->thread.lock.instance);
+          status = controller_instance_prepare_instance_type(main, instance->type, instance->action, dynamics[i]->array[j], &dependency);
 
-          if (status_lock != F_okay) {
-            controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-          }
-          else {
-            status = controller_instance_prepare_instance_type(main, instance->type, instance->action, dynamics[i]->array[j], &id_dependency);
+          if (F_status_is_error(status)) {
+            if (F_status_set_fine(status) == F_lock) {
+              if (!controller_thread_is_enabled_instance_type(&main->thread, instance->type)) return F_status_set_error(F_interrupt);
+            }
 
-            if (F_status_is_error(status)) {
-              if (F_status_set_fine(status) == F_lock) {
-                if (!controller_thread_is_enabled_instance_type(&main->thread, instance->type)) return F_status_set_error(F_interrupt);
-              }
+            controller_print_error_rule_item_rule_not_loaded(&main->program.error, &instance->cache.action, dynamics[i]->array[j]);
 
-              controller_print_error_rule_item_rule_not_loaded(&main->program.error, &instance->cache.action, dynamics[i]->array[j]);
+            return status;
+          }
 
-              return status;
-            }
+          status = found = F_true;
+
+          status_lock = controller_lock_read_instance(instance, &dependency->active);
 
-            status = found = F_true;
-            dependency = main->thread.instances.array[id_dependency];
+          if (F_status_is_error(status_lock)) {
+            controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
 
-            status_lock = controller_lock_read_instance(instance, &dependency->active);
+            status = F_false;
+            dependency = 0;
+          }
+          else {
+            status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
 
             if (F_status_is_error(status_lock)) {
               controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
 
               status = F_false;
-              dependency = 0;
-
-              f_thread_unlock(&main->thread.lock.instance);
             }
             else {
-              f_thread_unlock(&main->thread.lock.instance);
-
-              status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
-
-              if (F_status_is_error(status_lock)) {
-                controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
-                status = F_false;
-              }
-              else {
-                status = controller_rule_find(dynamics[i]->array[j], main->process.rules, &id_rule);
+              status = controller_rule_find(dynamics[i]->array[j], main->process.rules, &id_rule);
 
-                f_thread_unlock(&main->thread.lock.rule);
-              }
+              f_thread_unlock(&main->thread.lock.rule);
             }
           }
 
@@ -228,7 +214,7 @@ extern "C" {
 
             f_thread_unlock(&main->thread.lock.rule);
 
-            status_lock = controller_lock_read_instance(instance, &dependency->lock);
+            status_lock = controller_lock_read_instance(instance, &dependency->use);
 
             if (F_status_is_error(status_lock)) {
               controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
@@ -236,27 +222,37 @@ extern "C" {
               status = status_lock;
             }
             else if (dependency->state == controller_instance_state_active_e || dependency->state == controller_instance_state_busy_e) {
-              f_thread_unlock(&dependency->lock);
+              f_thread_unlock(&dependency->use);
 
               status = controller_instance_wait(dependency);
 
               if (F_status_is_error(status) && !(instance->options & controller_instance_option_simulate_e)) break;
 
-              status = dependency->rule.status[instance->action];
+              status_lock = controller_lock_read_instance(instance, &dependency->use);
+
+              if (F_status_is_error(status_lock)) {
+                controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+
+                status = status_lock;
+              }
+              else {
+                status = dependency->rule.status[instance->action];
+
+                f_thread_unlock(&dependency->use);
+              }
             }
             else {
+              f_thread_unlock(&dependency->use);
+
               status_lock = controller_lock_read_instance(instance, &main->thread.lock.rule);
 
               if (F_status_is_error(status_lock)) {
                 controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
 
-                f_thread_unlock(&dependency->lock);
-
                 status = status_lock;
               }
               else if (controller_rule_status_is_available(instance->action, main->process.rules.array[id_rule])) {
                 f_thread_unlock(&main->thread.lock.rule);
-                f_thread_unlock(&dependency->lock);
 
                 options_instance = 0;
 
@@ -296,7 +292,6 @@ extern "C" {
                 status = main->process.rules.array[id_rule].status[instance->action];
 
                 f_thread_unlock(&main->thread.lock.rule);
-                f_thread_unlock(&dependency->lock);
               }
             }
 
@@ -317,7 +312,9 @@ extern "C" {
             if (F_status_is_error(status_lock)) {
               status = status_lock;
 
-              controller_print_error_rule_instance_need_want_wish(&main->program.error, instance, strings[i], alias_other_buffer, "due to lock failure");
+              if (F_status_set_fine(status_lock) != F_interrupt) {
+                controller_print_error_rule_instance_need_want_wish(&main->program.error, instance, strings[i], alias_other_buffer, "due to lock failure");
+              }
             }
             else if (controller_rule_status_is_error(instance->action, main->process.rules.array[id_rule])) {
               f_thread_unlock(&main->thread.lock.rule);
@@ -399,17 +396,12 @@ extern "C" {
 
     f_number_unsigned_t id_rule = 0;
 
-    f_thread_unlock(&instance->lock);
-
-    status_lock = controller_lock_write_instance(instance, &instance->lock);
+    status_lock = controller_lock_write_instance(instance, &instance->use);
 
     if (F_status_is_error(status_lock)) {
       controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
 
-      status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
-      if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
-      return F_status_set_error(F_lock);
+      return status_lock;
     }
 
     if (F_status_is_error(status)) {
@@ -422,15 +414,11 @@ extern "C" {
     status_lock = controller_lock_write_instance(instance, &main->thread.lock.rule);
 
     if (F_status_is_error(status_lock)) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
-
-      // Remove the write lock and restore the read lock.
-      f_thread_unlock(&instance->lock);
+      f_thread_unlock(&instance->use);
 
-      status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
-      if (status_lock != F_okay) return F_status_set_error(F_lock_read);
+      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
 
-      return F_status_set_error(F_lock);
+      return F_status_set_error(status_lock);
     }
 
     // Update the Rule status, which is stored separately from the Rule status for this instance.
@@ -451,18 +439,7 @@ extern "C" {
     }
 
     f_thread_unlock(&main->thread.lock.rule);
-    f_thread_unlock(&instance->lock);
-
-    status_lock = controller_lock_read_instance(instance, &instance->lock);
-
-    if (F_status_is_error(status_lock)) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
-
-      status_lock = controller_lock_read_standard(instance->type != controller_instance_type_exit_e, controller_lock_check_flag_no_d, &main->thread, &instance->lock);
-      if (status_lock != F_okay) return F_status_set_error(F_lock_read);
-
-      return F_status_set_error(F_lock);
-    }
+    f_thread_unlock(&instance->use);
 
     return instance->rule.status[instance->action];
   }
@@ -474,80 +451,55 @@ extern "C" {
     if (!main || !cache) return F_status_set_error(F_parameter);
     if (!controller_thread_is_enabled_instance_type(&main->thread, type)) return F_status_set_error(F_interrupt);
 
-    f_status_t status = controller_lock_read_standard(type != controller_instance_type_exit_e, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
-
-    if (status != F_okay) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
-
-      return status;
-    }
-
-    f_number_unsigned_t at = 0;
+    controller_instance_t *instance = 0;
 
-    status = controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias_rule, &at);
+    f_status_t status = controller_instance_prepare(main, type != controller_instance_type_exit_e, action, alias_rule, &instance);
 
     if (F_status_is_error(status)) {
-      f_thread_unlock(&main->thread.lock.instance);
-
       controller_print_error_rule_item_rule_not_loaded(&main->program.error, &cache->action, alias_rule);
 
       return status;
     }
 
-    controller_instance_t * const instance = main->thread.instances.array[at];
-
     status = controller_lock_read_standard(type != controller_instance_type_exit_e, controller_lock_check_flag_yes_d, &main->thread, &instance->active);
 
     if (status != F_okay) {
       controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
       controller_print_error_rule_item(&main->program.error, &cache->action, F_false, F_status_set_fine(status));
 
-      f_thread_unlock(&main->thread.lock.instance);
-
       return status;
     }
 
-    f_status_t status_lock = controller_lock_write_instance(instance, &instance->lock);
-
-    if (F_status_is_error(status_lock)) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+    status = controller_lock_write_instance(instance, &instance->use);
 
+    if (F_status_is_error(status)) {
       f_thread_unlock(&instance->active);
-      f_thread_unlock(&main->thread.lock.instance);
 
-      return status_lock;
-    }
+      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
 
-    // Once a write lock on the instance is achieved, it is safe to unlock the instance read lock.
-    f_thread_unlock(&main->thread.lock.instance);
+      return status;
+    }
 
     // If the instance is already running, then there is nothing to do.
     if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e) {
-      f_thread_unlock(&instance->lock);
+      f_thread_unlock(&instance->use);
       f_thread_unlock(&instance->active);
 
       return F_busy;
     }
 
+    status = F_okay;
+
     // The thread is done, so close the thread.
     if (instance->state == controller_instance_state_done_e) {
-      controller_thread_join(&instance->id_thread);
-
-      f_thread_condition_signal_all(&instance->wait_condition);
-    }
-
-    instance->id = at;
-
-    f_thread_unlock(&instance->lock);
-
-    status_lock = controller_lock_write_instance(instance, &instance->lock);
-
-    if (F_status_is_error(status_lock)) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+      if (instance->thread) {
+        status = controller_thread_join(&instance->thread);
+      }
 
-      f_thread_unlock(&instance->active);
+      // Always set to idle and signal the wait condition because the thread is about to be replaced.
+      instance->state = controller_instance_state_idle_e;
 
-      return status_lock;
+      f_thread_condition_signal_all(&instance->wait_condition);
     }
 
     instance->state = controller_instance_state_active_e;
@@ -615,55 +567,73 @@ extern "C" {
       }
     }
 
-    f_thread_unlock(&instance->lock);
+    {
+      const uint8_t instance_action = instance->action;
 
-    if (F_status_is_error_not(status)) {
-      if (instance->action && (options_force & controller_instance_option_asynchronous_e)) {
-        if (instance->type == controller_instance_type_exit_e) {
-          status = f_thread_create(0, &instance->id_thread, controller_thread_instance_other, (void *) instance);
+      f_thread_unlock(&instance->use);
+
+      if (F_status_is_error_not(status)) {
+        if (instance_action && (options_force & controller_instance_option_asynchronous_e)) {
+          if (instance->type == controller_instance_type_exit_e) {
+            status = f_thread_create(0, &instance->thread, controller_thread_instance_other, (void *) instance);
+          }
+          else {
+            status = f_thread_create(0, &instance->thread, controller_thread_instance_normal, (void *) instance);
+          }
+
+          if (F_status_is_error(status)) {
+            controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), F_status_set_fine(status));
+          }
         }
         else {
-          status = f_thread_create(0, &instance->id_thread, controller_thread_instance_normal, (void *) instance);
-        }
+          status = controller_rule_instance_perform(instance, options_force);
 
-        if (F_status_is_error(status)) {
-          controller_print_error_status(&main->program.error, macro_controller_f(f_thread_create), F_status_set_fine(status));
+          if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+            f_thread_unlock(&instance->active);
+
+            return status;
+          }
         }
       }
-      else {
-        status = controller_rule_instance_perform(instance, options_force);
+    }
 
-        if (status == F_child || F_status_set_fine(status) == F_interrupt) {
-          f_thread_unlock(&instance->active);
+    status = controller_lock_read_instance(instance, &instance->use);
 
-          return status;
-        }
-      }
+    if (F_status_is_error(status)) {
+      f_thread_unlock(&instance->active);
+
+      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_true);
+
+      return status;
     }
 
-    if (!action || F_status_is_error(status) && (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) {
-      {
-        status_lock = controller_lock_write_instance(instance, &instance->lock);
+    {
+      const uint8_t instance_state = instance->state;
+
+      if (!action || F_status_is_error(status) && (instance_state == controller_instance_state_active_e || instance_state == controller_instance_state_busy_e)) {
+        f_thread_unlock(&instance->use);
 
-        if (F_status_is_error(status_lock)) {
-          controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+        status = controller_lock_write_instance(instance, &instance->use);
 
+        if (F_status_is_error(status)) {
           f_thread_unlock(&instance->active);
 
-          return status_lock;
+          controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status), F_false);
+
+          return status;
         }
-      }
 
-      if (!action || (options_force & controller_instance_option_asynchronous_e)) {
-        instance->state = controller_instance_state_done_e;
-      }
-      else {
-        instance->state = controller_instance_state_idle_e;
-      }
+        if (!action || (options_force & controller_instance_option_asynchronous_e)) {
+          instance->state = controller_instance_state_done_e;
+        }
+        else {
+          instance->state = controller_instance_state_idle_e;
+        }
 
-      f_thread_condition_signal_all(&instance->wait_condition);
+        f_thread_condition_signal_all(&instance->wait_condition);
+      }
 
-      f_thread_unlock(&instance->lock);
+      f_thread_unlock(&instance->use);
     }
 
     f_thread_unlock(&instance->active);
@@ -689,26 +659,14 @@ extern "C" {
           return status_lock;
         }
       }
-
-      status_lock = controller_lock_read_instance(instance, &instance->lock);
-
-      if (F_status_is_error(status_lock)) {
-        controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status_lock), F_true);
-
-        if (options_force & controller_instance_option_asynchronous_e) {
-          f_thread_unlock(&instance->active);
-        }
-
-        return status_lock;
-      }
     }
 
-    uint8_t flag = 0x2;
+    uint8_t flag = 0x0;
 
     const f_status_t status = controller_rule_instance_perform_details(instance, options_force, &flag);
 
-    if (flag & 0x2) {
-      f_thread_unlock(&instance->lock);
+    if (flag & 0x4) {
+      f_thread_unlock(&instance->main->thread.lock.rule);
     }
 
     if (options_force & controller_instance_option_asynchronous_e) {
@@ -716,7 +674,7 @@ extern "C" {
     }
 
     if (flag & 0x1) {
-      controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status), !(flag & 0x4));
+      controller_print_error_lock_critical(&instance->main->program.error, F_status_set_fine(status), !(flag & 0x2));
     }
 
     return status;
@@ -728,12 +686,7 @@ extern "C" {
 
     if (!instance || !instance->main) return F_status_set_error(F_parameter);
 
-    f_status_t status = F_okay;
-    f_number_unsigned_t id_rule = 0;
-
-    const f_number_unsigned_t used_original_stack = instance->stack.used;
-
-    f_status_t status_lock = controller_lock_read_instance(instance, &instance->main->thread.lock.rule); // @fixme Wrong lock function?
+    f_status_t status_lock = controller_lock_read_instance(instance, &instance->use);
 
     if (F_status_is_error(status_lock)) {
       *flag |= 0x1;
@@ -741,40 +694,35 @@ extern "C" {
       return status_lock;
     }
 
-    if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
-      f_thread_unlock(&instance->lock);
+    f_status_t status = F_okay;
+    f_number_unsigned_t id_rule = 0;
+
+    const f_number_unsigned_t used_original_stack = instance->stack.used;
+
+    f_thread_unlock(&instance->use);
 
-      *flag &= ~0x2;
+    status_lock = controller_lock_read_instance(instance, &instance->main->thread.lock.rule);
+    if (F_status_is_error(status_lock)) return status_lock;
 
-      status_lock = controller_lock_write_instance(instance, &instance->lock);
+    *flag |= 0x4;
+
+    if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
+      status_lock = controller_lock_write_instance(instance, &instance->use);
 
       if (F_status_is_error(status_lock)) {
-        *flag |= 0x5;
+        *flag |= 0x3;
 
         return status_lock;
       }
 
-      *flag |= 0x2;
-
       controller_rule_delete(&instance->rule);
 
       status = controller_rule_copy(instance->main->process.rules.array[id_rule], &instance->rule);
 
-      f_thread_unlock(&instance->lock);
-
-      *flag &= ~0x2;
-
-      status_lock = controller_lock_read_instance(instance, &instance->lock);
-
+      f_thread_unlock(&instance->use);
       f_thread_unlock(&instance->main->thread.lock.rule);
 
-      if (F_status_is_error(status_lock)) {
-        *flag |= 0x1;
-
-        return status_lock;
-      }
-
-      *flag |= 0x2;
+      *flag &= ~0x4;
 
       if (F_status_is_error(status)) {
         controller_print_error_status(&instance->main->program.error, macro_controller_f(controller_rule_copy), F_status_set_fine(status));
@@ -785,6 +733,14 @@ extern "C" {
         return F_process_not;
       }
       else {
+        status_lock = controller_lock_read_instance(instance, &instance->use);
+
+        if (F_status_is_error(status_lock)) {
+          *flag |= 0x1;
+
+          return status_lock;
+        }
+
         for (f_number_unsigned_t i = 0; i < instance->stack.used && controller_thread_is_enabled_instance(instance); ++i) {
 
           if (instance->stack.array[i] == id_rule) {
@@ -798,41 +754,29 @@ extern "C" {
           }
         } // for
 
+        f_thread_unlock(&instance->use);
+
         if (!controller_thread_is_enabled_instance(instance)) return F_status_set_error(F_interrupt);
 
         if (F_status_is_error_not(status)) {
+          status_lock = controller_lock_write_instance(instance, &instance->use);
+
+          if (F_status_is_error(status_lock)) {
+            *flag |= 0x3;
+
+            return status_lock;
+          }
+
           status = f_memory_array_increase(controller_allocation_small_d, sizeof(f_number_unsigned_t), (void **) &instance->stack.array, &instance->stack.used, &instance->stack.size);
 
           if (F_status_is_error(status)) {
             controller_print_error_status(&instance->main->program.error, macro_controller_f(f_memory_array_increase), F_status_set_fine(status));
           }
           else {
-            f_thread_unlock(&instance->lock);
-
-            *flag &= ~0x2;
-
-            status_lock = controller_lock_write_instance(instance, &instance->lock);
-
-            if (F_status_is_error(status_lock)) {
-              *flag |= 0x5;
-
-              return status_lock;
-            }
-
             instance->stack.array[instance->stack.used++] = id_rule;
-
-            f_thread_unlock(&instance->lock);
-
-            status_lock = controller_lock_read_instance(instance, &instance->lock);
-
-            if (F_status_is_error(status_lock)) {
-              *flag |= 0x1;
-
-              return status_lock;
-            }
-
-            *flag |= 0x2;
           }
+
+          f_thread_unlock(&instance->use);
         }
       }
 
@@ -841,11 +785,13 @@ extern "C" {
       }
     }
     else {
-      f_thread_unlock(&instance->main->thread.lock.rule);
-
       status = F_status_set_error(F_found_not);
 
       controller_print_error_rule_item_rule_not_loaded(&instance->main->program.error, &instance->cache.action, instance->rule.alias);
+
+      f_thread_unlock(&instance->main->thread.lock.rule);
+
+      *flag &= ~0x4;
     }
 
     if (status == F_child) return status;
@@ -853,15 +799,13 @@ extern "C" {
     status_lock = controller_lock_write_instance(instance, &instance->main->thread.lock.rule);
 
     if (F_status_is_error(status_lock)) {
-      *flag |= 0x5;
-
-      if (F_status_set_fine(status) == F_lock) {
-        *flag &= ~0x2;
-      }
+      *flag |= 0x3;
 
       return status_lock;
     }
 
+    *flag |= 0x4;
+
     if (F_status_set_fine(status) == F_lock) {
       if (controller_rule_find(instance->rule.alias, instance->main->process.rules, &id_rule) == F_true) {
         instance->main->process.rules.array[id_rule].status[instance->action] = status;
@@ -870,18 +814,16 @@ extern "C" {
 
     f_thread_unlock(&instance->main->thread.lock.rule);
 
-    if (F_status_set_fine(status) == F_lock) {
-      *flag &= ~0x2;
-    }
+    *flag &= ~0x4;
 
-    if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_instance(instance)) {
+    if (F_status_set_fine(status) == F_interrupt || (F_status_set_fine(status) == F_lock || F_status_set_fine(status) == F_lock_read || F_status_set_fine(status) == F_lock_write) && !controller_thread_is_enabled_instance(instance)) {
       return F_status_set_error(F_interrupt);
     }
 
-    status_lock = controller_lock_write_instance(instance, &instance->lock);
+    status_lock = controller_lock_write_instance(instance, &instance->use);
 
     if (F_status_is_error(status_lock)) {
-      *flag |= 0x5;
+      *flag |= 0x3;
 
       return status_lock;
     }
@@ -889,6 +831,8 @@ extern "C" {
     instance->state = (options_force & controller_instance_option_asynchronous_e) ? controller_instance_state_done_e : controller_instance_state_idle_e;
     instance->stack.used = used_original_stack;
 
+    f_thread_unlock(&instance->use);
+
     f_thread_condition_signal_all(&instance->wait_condition);
 
     return controller_thread_is_enabled_instance(instance) ? status : F_status_set_error(F_interrupt);
index ed8d43d8d48cd58f951e8f4e386e5743d08a4485..077b58dda46e9ee636b83aaa53469c8dfe23bb78 100644 (file)
@@ -22,7 +22,7 @@ extern "C" {
  * Any dependent rules are processed and executed as per "need", "want", and "wish" Rule settings.
  * All dependent rules must be already loaded, this function will not load any rules.
  *
- * This requires that a read lock be set on instance->lock before being called.
+ * This requires that a read lock be set on instance.use before being called.
  *
  * This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_number_unsigneds_t array.
  *
@@ -39,8 +39,8 @@ extern "C" {
  *   F_failure on execution failure.
  *
  *   F_interrupt (with error bit) on receiving a instance signal, such as an interrupt signal.
- *   F_lock (with error bit) if failed to establish a lock, and the instance->lock read lock is already restored.
- *   F_lock_read (with error bit) if failed to re-establish read lock on instance->lock while returning.
+ *   F_lock (with error bit) if failed to establish a lock, and the instance.use read lock is already restored.
+ *   F_lock_read (with error bit) if failed to re-establish read lock on instance.use while returning.
  *
  *   Errors (with error bit) from: controller_lock_read_standard().
  *   Errors (with error bit) from: controller_lock_write_instance().
@@ -139,6 +139,9 @@ extern "C" {
  * This does all the preparation work that needs to be synchronously performed within the same thread.
  * This will copy the Rule by the alias to the instance structure.
  *
+ * The "active" lock may be locked before calling this (must be locked if asynchronous).
+ * The "lock" lock must be locked before calling this.
+ *
  * @param instance
  *   The instance data.
  *
@@ -151,8 +154,8 @@ extern "C" {
  * @param flag
  *   Designate lock states:
  *     - 0x1: Locking error.
- *     - 0x2: The instance lock is set.
- *     - 0x4: Locking error is a write lock.
+ *     - 0x2: Locking error is a write lock.
+ *     - 0x4: The rule lock is still set.
  *
  *   Must not be NULL.
  *
index b1c6bd587e6e025b4806c7e07d8b99fa7272b008..4969a6be467ae8f075c49e5b20d0ec2c67523b86 100644 (file)
@@ -9,6 +9,8 @@ extern "C" {
 
     if (!main) return F_status_set_error(F_parameter);
 
+    if (!controller_thread_enable_is_normal(&main->thread, is_normal)) return F_status_set_error(F_interrupt);
+
     f_status_t status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_no_d, &main->thread, &main->thread.lock.instance);
 
     if (status_lock != F_okay) {
@@ -24,8 +26,11 @@ extern "C" {
     }
 
     f_status_t status = F_okay;
+    f_status_t status_join = F_okay;
+
     uint8_t required_not_run = F_false;
     uint8_t skip = F_false;
+    uint8_t locked = 0x0; // 0x1 = active (read), 0x2 = lock (read), 0x4 lock (write), 0x8 = continue loop.
 
     f_number_unsigned_t i = 0;
     f_number_unsigned_t j = 0;
@@ -34,103 +39,68 @@ extern "C" {
     const f_number_unsigned_t instance_total = main->thread.instances.used;
     controller_instance_t *instance_list[instance_total];
 
-    for (; i < instance_total; ++i) {
-      instance_list[i] = main->thread.instances.array[i];
-    } // for
+    memcpy((void *) instance_list, (void *) main->thread.instances.array, sizeof(controller_instance_t *) * instance_total);
 
     f_thread_unlock(&main->thread.lock.instance);
 
-    for (i = 0; i < instance_total; ++i) {
+    for (; i < instance_total; ++i) {
 
       if (!controller_thread_enable_is_normal(&main->thread, is_normal)) break;
-
-      // Re-establish instance read lock to wait for or protect from the cleanup thread while checking the read instance.
-      status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &main->thread.lock.instance);
-      if (status_lock != F_okay) break;
-
-      if (!instance_list[i]) {
-        f_thread_unlock(&main->thread.lock.instance);
-
-        continue;
-      }
+      if (!instance_list[i]) continue;
 
       status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->active);
+      if (status_lock != F_okay) break;
 
-      if (status_lock != F_okay) {
-        f_thread_unlock(&main->thread.lock.instance);
-
-        break;
-      }
-
-      // Once the active lock is obtained, then the main instance read lock can be safely released.
-      f_thread_unlock(&main->thread.lock.instance);
-
-      status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
-
-      if (status_lock != F_okay) {
-        f_thread_unlock(&instance_list[i]->active);
+      locked = 0x1;
 
-        break;
-      }
+      status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+      if (status_lock != F_okay) break;
 
-      if (required) {
-        if (!(instance_list[i]->options & controller_instance_option_require_e)) {
-          f_thread_unlock(&instance_list[i]->lock);
-          f_thread_unlock(&instance_list[i]->active);
+      locked |= 0x2;
 
-          continue;
-        }
+      // If the instance thread has been cleaned up and joined or if instance is not required when only processing "required", then skip.
+      if (!instance_list[i]->thread || required && !(instance_list[i]->options & controller_instance_option_require_e)) {
+        locked |= 0x8;
       }
 
-      if (!instance_list[i]->state || instance_list[i]->state == controller_instance_state_idle_e || instance_list[i]->state == controller_instance_state_done_e) {
-
+      if (!(locked & 0x8) && (!instance_list[i]->state || instance_list[i]->state == controller_instance_state_idle_e || instance_list[i]->state == controller_instance_state_done_e)) {
         if (instance_list[i]->state == controller_instance_state_done_e) {
-          f_thread_unlock(&instance_list[i]->lock);
 
-          status_lock = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+          // Release use lock before waiting for thread to join.
+          f_thread_unlock(&instance_list[i]->use);
 
-          if (status_lock != F_okay) {
-            controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_false);
+          locked &= ~0x2;
+          status_join = F_data_not;
 
-            f_thread_unlock(&instance_list[i]->active);
-
-            return status_lock;
+          if (instance_list[i]->thread) {
+            status_join = controller_thread_join(&instance_list[i]->thread);
           }
 
-          if (instance_list[i]->state == controller_instance_state_done_e) {
-            f_thread_unlock(&instance_list[i]->active);
-
-            if (f_thread_lock_write_try(&instance_list[i]->active) == F_okay) {
-              controller_thread_join(&instance_list[i]->id_thread);
+          if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
+            status_lock = controller_lock_write_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+            if (status_lock != F_okay) break;
 
-              instance_list[i]->state = controller_instance_state_idle_e;
+            locked |= 0x4;
 
-              f_thread_unlock(&instance_list[i]->active);
-              f_thread_condition_signal_all(&instance_list[i]->wait_condition);
-            }
+            instance_list[i]->state = controller_instance_state_idle_e;
 
-            status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->active);
+            f_thread_unlock(&instance_list[i]->use);
 
-            if (status_lock != F_okay) {
-              f_thread_unlock(&instance_list[i]->lock);
+            locked &= ~0x4;
 
-              break;
-            }
+            f_thread_condition_signal_all(&instance_list[i]->wait_condition);
           }
 
-          f_thread_unlock(&instance_list[i]->lock);
-
-          status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+          status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
           if (status_lock != F_okay) break;
+
+          locked |= 0x2;
         }
 
         if (instance_list[i]->options & controller_instance_option_require_e) {
           if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
             status = F_status_set_error(F_require);
 
-            f_thread_unlock(&instance_list[i]->lock);
-            f_thread_unlock(&instance_list[i]->active);
-
             break;
           }
           else if (controller_rule_status_is_available(instance_list[i]->action, instance_list[i]->rule)) {
@@ -138,59 +108,50 @@ extern "C" {
           }
         }
 
-        f_thread_unlock(&instance_list[i]->lock);
-        f_thread_unlock(&instance_list[i]->active);
-
+        // Break on required and continue otherwise.
         if (F_status_set_fine(status) == F_require) break;
 
-        continue;
+        locked |= 0x8;
       }
 
-      if (!controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule) && (instance_list[i]->state == controller_instance_state_active_e || instance_list[i]->state == controller_instance_state_busy_e)) {
-        f_thread_unlock(&instance_list[i]->lock);
+      if (!(locked & 0x8) && (!controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule) && (instance_list[i]->state == controller_instance_state_active_e || instance_list[i]->state == controller_instance_state_busy_e))) {
 
-        status = controller_instance_wait(instance_list[i]);
-
-        if (F_status_set_fine(status) == F_interrupt) {
-          f_thread_unlock(&instance_list[i]->active);
+        // Release use lock before waiting for instance to complete.
+        f_thread_unlock(&instance_list[i]->use);
 
-          break;
-        }
+        locked &= ~0x2;
 
-        status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->lock);
+        status = controller_instance_wait(instance_list[i]);
+        if (F_status_set_fine(status) == F_interrupt) break;
 
-        if (status_lock != F_okay) {
-          f_thread_unlock(&instance_list[i]->active);
+        status_lock = controller_lock_read_standard(is_normal, controller_lock_check_flag_yes_d, &main->thread, &instance_list[i]->use);
+        if (status_lock != F_okay) break;
 
-          break;
-        }
-
-        if ((instance_list[i]->options & controller_instance_option_require_e)) {
-          f_thread_unlock(&instance_list[i]->lock);
+        locked |= 0x2;
 
+        if (instance_list[i]->options & controller_instance_option_require_e) {
           if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
             status = F_status_set_error(F_require);
 
-            f_thread_unlock(&instance_list[i]->active);
-
             break;
           }
         }
-        else {
-          f_thread_unlock(&instance_list[i]->lock);
-        }
-      }
-      else {
-        f_thread_unlock(&instance_list[i]->lock);
       }
 
-      f_thread_unlock(&instance_list[i]->active);
-
       if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_require) break;
+      if (locked & 0x6) f_thread_unlock(&instance_list[i]->use);
+      if (locked & 0x1) f_thread_unlock(&instance_list[i]->active);
+
+      locked = 0;
     } // for
 
+    if (i < instance_total) {
+      if (locked & 0x6) f_thread_unlock(&instance_list[i]->use);
+      if (locked & 0x1) f_thread_unlock(&instance_list[i]->active);
+    }
+
     if (F_status_is_error(status_lock)) {
-      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), F_true);
+      controller_print_error_lock_critical(&main->program.error, F_status_set_fine(status_lock), !(locked & 0x4));
 
       return status_lock;
     }
index 1e1bbc4b5863500c396cffab97f198f76b6cf02f..6c517773ed2045f5da747d69e47055b303d4851c 100644 (file)
@@ -62,60 +62,42 @@ extern "C" {
         break;
       }
 
-      if (f_thread_lock_write_try(&main->thread.lock.instance) == F_okay) {
-        controller_instance_t *instance = 0;
+      {
+        f_time_spec_t time;
 
-        f_number_unsigned_t i = 0;
+        if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
+          status = F_enable_not;
 
-        for (status = F_okay; i < main->thread.instances.used && controller_thread_enable_get(&main->thread) == controller_thread_enable_e; ++i) {
+          break;
+        }
 
-          if (!main->thread.instances.array[i]) continue;
-
-          instance = main->thread.instances.array[i];
+        memset((void *) &time, 0, sizeof(struct timespec));
 
-          {
-            f_time_spec_t time;
+        controller_time_now(controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &time);
 
-            do {
-              if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
-                status = F_enable_not;
-
-                break;
-              }
+        status = f_thread_mutex_lock_timed(&time, &main->thread.lock.cancel.mutex);
+        if (status != F_okay) continue;
+      }
 
-              memset((void *) &time, 0, sizeof(struct timespec));
+      if (f_thread_lock_write_try(&main->thread.lock.instance) == F_okay) {
+        controller_instance_t *instance = 0;
 
-              controller_time_now(controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &time);
+        f_number_unsigned_t i = 0;
 
-              status = f_thread_mutex_lock_timed(&time, &main->thread.lock.cancel.mutex);
-              if (F_status_is_error(status)) break;
+        for (status = F_okay; i < main->thread.instances.used && controller_thread_enable_get(&main->thread) == controller_thread_enable_e; ++i) {
 
-            } while (status != F_okay);
+          if (!main->thread.instances.array[i]) continue;
 
-            if (status == F_enable_not) break;
-            if (F_status_is_error(status)) continue;
-          }
+          instance = main->thread.instances.array[i];
 
           // If "active" has a read or write lock, then do not attempt to clean it.
-          if (f_thread_lock_write_try(&instance->active) != F_okay) {
-            f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
-
-            continue;
-          }
-
-          // If "lock" has a read or write lock, then do not attempt to clean it.
-          if (f_thread_lock_write_try(&instance->lock) != F_okay) {
-            f_thread_unlock(&instance->active);
-            f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
-
-            continue;
-          }
+          // Once a write lock is achieved on "active", then there should only ever be one process operating on it.
+          // Therefore, the instance.use lock does not need to be used in any way.
+          if (f_thread_lock_write_try(&instance->active) != F_okay) continue;
 
           // If instance is active or busy, then do not attempt to clean it.
           if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e || controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
-            f_thread_unlock(&instance->lock);
             f_thread_unlock(&instance->active);
-            f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
 
             if (controller_thread_enable_get(&main->thread) == controller_thread_enable_e) continue;
 
@@ -134,9 +116,7 @@ extern "C" {
             } // for
 
             if (j < instance->path_pids.used || controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
-              f_thread_unlock(&instance->lock);
               f_thread_unlock(&instance->active);
-              f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
 
               if (controller_thread_enable_get(&main->thread) == controller_thread_enable_e) continue;
 
@@ -145,19 +125,16 @@ extern "C" {
           }
 
           // Close any still open thread.
-          if (instance->id_thread) {
-            status = f_thread_join(instance->id_thread, 0);
+          if (instance->thread) {
+            status = controller_thread_join(&instance->thread);
 
             if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
               instance->state = controller_instance_state_idle_e;
-              instance->id_thread = 0;
 
               f_thread_condition_signal_all(&instance->wait_condition);
             }
             else {
-              f_thread_unlock(&instance->lock);
               f_thread_unlock(&instance->active);
-              f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
 
               continue;
             }
@@ -174,9 +151,7 @@ extern "C" {
             } // for
 
             if (controller_thread_enable_get(&main->thread) != controller_thread_enable_e) {
-              f_thread_unlock(&instance->lock);
               f_thread_unlock(&instance->active);
-              f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
 
               break;
             }
@@ -198,13 +173,13 @@ extern "C" {
             controller_rule_delete(&instance->rule);
           }
 
-          f_thread_unlock(&instance->lock);
           f_thread_unlock(&instance->active);
-          f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
         } // for
 
         f_thread_unlock(&main->thread.lock.instance);
       }
+
+      f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
     } // while
 
     return 0;
index 94129d1dd085ada06998d7105ebe0072b59795fa..0bf97ab8369e39359983cac1eb48b9b26488ced0 100644 (file)
@@ -28,7 +28,7 @@ extern "C" {
 
     if (!main) return;
 
-    f_status_t status = controller_mutex_lock(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.cancel);
+    f_status_t status = controller_mutex_lock(is_normal, controller_lock_check_flag_time_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.cancel);
     if (F_status_is_error(status)) return;
 
     // Only cancel when enabled.
@@ -54,22 +54,22 @@ extern "C" {
     time.tv_nsec = interval_nanoseconds;
 
     if (main->process.mode == controller_process_mode_helper_e && !(main->setting.flag & controller_main_flag_validate_d)) {
-      controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
+      if (F_status_is_error_not(controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance))) {
+        for (i = 0; i < main->thread.instances.used; ++i) {
 
-      for (i = 0; i < main->thread.instances.used; ++i) {
+          if (!main->thread.instances.array[i]) continue;
 
-        if (!main->thread.instances.array[i]) continue;
-
-        instance = main->thread.instances.array[i];
+          instance = main->thread.instances.array[i];
 
-        if (!instance->id_thread) continue;
+          if (!instance->thread) continue;
 
-        controller_thread_detach(&instance->id_thread);
+          controller_thread_detach(&instance->thread);
 
-        instance->id_thread = 0;
-      } // for
+          instance->thread = 0;
+        } // for
 
-      f_thread_unlock(&main->thread.lock.instance);
+        f_thread_unlock(&main->thread.lock.instance);
+      }
     }
 
     {
@@ -116,6 +116,7 @@ extern "C" {
       return;
     }
 
+    // Ensure that the instances cannot be changed during the cancellation process by establishing a read lock.
     status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
 
     if (F_status_is_error(status)) {
@@ -135,6 +136,17 @@ extern "C" {
         continue;
       }
 
+      status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+      if (F_status_is_error(status)) continue;
+
+      status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+      if (F_status_is_error(status)) {
+        f_thread_unlock(&instance->active);
+
+        continue;
+      }
+
       for (j = 0; j < instance->childs.used; ++j) {
 
         if (instance->childs.array[j] > 0) {
@@ -156,15 +168,14 @@ extern "C" {
           }
         }
       } // for
-    } // for
 
-    f_thread_unlock(&main->thread.lock.instance);
+      f_thread_unlock(&instance->use);
+      f_thread_unlock(&instance->active);
+    } // for
 
     if (entry->timeout_exit && !(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
       f_number_unsigned_t lapsed = 0;
 
-      controller_lock_read(is_normal, controller_lock_check_flag_no_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
-
       for (i = 0; i < main->thread.instances.used && lapsed < entry->timeout_exit; ++i) {
 
         if (!main->thread.instances.array[i]) continue;
@@ -176,6 +187,17 @@ extern "C" {
           continue;
         }
 
+        status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+        if (F_status_is_error(status)) continue;
+
+        status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+        if (F_status_is_error(status)) {
+          f_thread_unlock(&instance->active);
+
+          continue;
+        }
+
         for (j = 0; j < instance->childs.used && lapsed < entry->timeout_exit; ++j) {
 
           while (instance->childs.array[j] > 0 && lapsed < entry->timeout_exit) {
@@ -223,28 +245,21 @@ extern "C" {
             }
           }
         } // for
-      } // for
-
-      f_thread_unlock(&main->thread.lock.instance);
-    }
-
-    status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.lock.instance);
-
-    if (F_status_is_error(status)) {
-      f_thread_mutex_unlock(&main->thread.lock.cancel.mutex);
 
-      return;
+        f_thread_unlock(&instance->use);
+        f_thread_unlock(&instance->active);
+      } // for
     }
 
     for (i = 0; i < main->thread.instances.size; ++i) {
 
       if (!main->thread.instances.array[i]) continue;
 
-      status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &main->thread.instances.array[i]->active);
-      if (F_status_is_error(status)) continue;
-
       instance = main->thread.instances.array[i];
 
+      status = controller_lock_read(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->active);
+      if (F_status_is_error(status)) continue;
+
       // Do not kill Exit Instances, when not performing "execute" during exit.
       if (instance->type == controller_instance_type_exit_e && controller_thread_enable_get(&main->thread) != controller_thread_enable_exit_execute_e) {
         f_thread_unlock(&instance->active);
@@ -252,7 +267,15 @@ extern "C" {
         continue;
       }
 
-      if (instance->id_thread) {
+      if (instance->thread) {
+        status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+        if (F_status_is_error(status)) {
+          f_thread_unlock(&instance->active);
+
+          continue;
+        }
+
         if (instance->childs.used) {
           for (j = 0; j < instance->childs.used; ++j) {
 
@@ -269,12 +292,21 @@ extern "C" {
           f_time_sleep_spec(time, 0);
         }
 
-        f_thread_join(instance->id_thread, 0);
+        f_thread_unlock(&instance->use);
+        f_thread_join(instance->thread, 0);
 
-        instance->id_thread = 0;
+        instance->thread = 0;
       }
 
       if (!(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
+        status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+        if (F_status_is_error(status)) {
+          f_thread_unlock(&instance->active);
+
+          continue;
+        }
+
         for (j = 0; j < instance->childs.size; ++j) {
 
           // Do not kill Exit processes, when not performing "execute" during exit.
@@ -287,16 +319,24 @@ extern "C" {
               f_signal_send(F_signal_kill, instance->childs.array[j]);
             }
 
-            status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
-
-            instance->childs.array[j] = 0;
-
-            if (F_status_is_error_not(status)) f_thread_unlock(&instance->lock);
+            if (F_status_is_error_not(status)) {
+              instance->childs.array[j] = 0;
+            }
           }
         } // for
+
+        f_thread_unlock(&instance->use);
       }
 
       if (!(entry->flag & controller_entry_flag_timeout_exit_no_e)) {
+        status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
+
+        if (F_status_is_error(status)) {
+          f_thread_unlock(&instance->active);
+
+          continue;
+        }
+
         for (j = 0; j < instance->path_pids.used; ++j) {
 
           // Do not kill Exit processes, when not performing "execute" during exit.
@@ -309,17 +349,18 @@ extern "C" {
               f_signal_send(F_signal_kill, pid);
             }
 
-            status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
-
-            f_file_remove(instance->path_pids.array[j]);
-            instance->path_pids.array[j].used = 0;
+            if (F_status_is_error_not(status)) {
+              f_file_remove(instance->path_pids.array[j]);
 
-            if (F_status_is_error_not(status)) f_thread_unlock(&instance->lock);
+              instance->path_pids.array[j].used = 0;
+            }
           }
         } // for
+
+        f_thread_unlock(&instance->use);
       }
 
-      status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->lock);
+      status = controller_lock_write(is_normal, controller_lock_check_flag_error_d, controller_thread_timeout_cancel_seconds_d, controller_thread_timeout_cancel_nanoseconds_d, &main->thread, &instance->use);
 
       if (F_status_is_error_not(status)) {
 
@@ -343,7 +384,7 @@ extern "C" {
           --instance->path_pids.used;
         } // while
 
-        f_thread_unlock(&instance->lock);
+        f_thread_unlock(&instance->use);
       }
 
       f_thread_unlock(&instance->active);