]> Kevux Git Server - fll/commitdiff
Bugfix: improve cancellation point processing and handling.
authorKevin Day <thekevinday@gmail.com>
Sat, 10 Apr 2021 15:35:00 +0000 (10:35 -0500)
committerKevin Day <thekevinday@gmail.com>
Sat, 10 Apr 2021 15:58:00 +0000 (10:58 -0500)
This avoids the use of f_thread_cancel_test() which is horribly limited in design.
Because f_thread_cancel_test() never returns when thread is in cancelled state, the caller cannot properly handle the situation.
This limits the code design in order to properly use f_thread_cancel_test().

There are already cancellation points in place due to the extensive use of thread.enabled.
Improve these cancellation points, adding timed checks to write locks to further check if a cancellation was received (via thread.enabled).
The timeout is arbitrarily pick as "0.1 seconds".
Hopefully, this doesn't make things too busy, but really the write locks should (ideally) never have to be waiting that long anyway.

Remove stale code in private-common.c involving a write lock and then an immediate unlock on deallocation.

Add additional thread.enabled checks.

There were some places where cancellation points were being returned but were not properly unlocking all held locks that are within the scope of the function.

All threads should now be set to PTHREAD_CANCEL_DEFERRED.

The forced thread termination via kill signals are now removed.
They shouldn't be needed now that cancellation (should be) guaranteed.
This will have to be tested over time to confirm the truth of.

level_3/controller/c/private-common.c
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-rule.c
level_3/controller/c/private-thread.c

index 08edc83e44e0e9b769373ec6a87e3d2f5eb784c7..888cec0240a59e40314cdd25e42210f25fd2885a 100644 (file)
@@ -140,9 +140,6 @@ extern "C" {
 
     if (F_status_is_error(status)) {
       if (F_status_set_fine(status) == F_busy) {
-        f_thread_lock_write(lock);
-        f_thread_unlock(lock);
-
         if (f_thread_lock_delete(lock) == F_none) {
           lock = 0;
         }
@@ -163,6 +160,32 @@ extern "C" {
   }
 #endif // _di_controller_lock_delete_simple_
 
+#ifndef _di_controller_lock_write_
+  f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+    struct timespec time;
+    time.tv_sec = 0;
+    time.tv_nsec = controller_thread_lock_timeout;
+
+    f_status_t status = F_none;
+
+    for (;;) {
+      status = f_thread_lock_write_timed(&time, lock);
+
+      if (status == F_time) {
+        if (!thread->enabled) {
+          return F_signal;
+        }
+      }
+      else {
+        break;
+      }
+    } // for
+
+    return status;
+  }
+#endif // _di_controller_lock_write_
+
 #ifndef _di_controller_print_unlock_flush_
   void controller_print_unlock_flush(FILE * const stream, f_thread_mutex_t *mutex) {
 
index 83278b50e5d12db6f035311fa8061d7518c417d6..32b6321cf24b7a6482e40fb6eea5ca51d15f4686 100644 (file)
@@ -1077,6 +1077,7 @@ extern "C" {
   #define controller_thread_cleanup_interval_short 180  // 3 minutes in seconds.
   #define controller_thread_exit_process_cancel_wait 60000000 // 0.06 seconds in nanoseconds.
   #define controller_thread_exit_process_cancel_total 150 // 9 seconds in multiples of wait.
+  #define controller_thread_lock_timeout 100000000 // 0.1 seconds in nanoseconds.
   #define controller_thread_simulation_timeout 200000 // 0.2 seconds in microseconds.
   #define controller_thread_wait_timeout_seconds 10
   #define controller_thread_wait_timeout_nanoseconds 0
@@ -1315,6 +1316,30 @@ extern "C" {
 #endif // _di_controller_lock_delete_simple_
 
 /**
+ * Wait to get a write lock.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param lock
+ *   The r/w lock to obtain a write lock on.
+ * @param thread
+ *   The thread data used to determine if the main thread is disabled or not.
+ *
+ * @return
+ *   F_none on success.
+ *   F_status if main thread is disabled and write lock was never achieved.
+ *
+ *   Status from: f_thread_lock_write_timed().
+ *
+ *   Errors (with error bit) from: f_thread_lock_write_timed().
+ *
+ * @see f_thread_lock_write_timed()
+ */
+#ifndef _di_controller_lock_write_
+  extern f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_lock_write_
+
+/**
  * Flush the stream buffer and then unlock the mutex.
  *
  * Weird behavior was observed when piping data from this program.
index ca34fe7d6eab6a2109121952085fd546143039c7..df0e2fe3028c607681fbcd50b6bb2087af7b736c 100644 (file)
@@ -437,7 +437,7 @@ extern "C" {
       return status;
     }
 
-    for (;;) {
+    for (; main.thread->enabled; ) {
 
       actions = &main.setting->entry.items.array[cache->ats.array[at_i]].actions;
 
@@ -605,6 +605,10 @@ extern "C" {
       }
     } // for
 
+    if (!main.thread->enabled) {
+      return F_signal;
+    }
+
     // if ready was never found in the entry, then default to always ready.
     if (main.setting->ready == controller_setting_ready_no) {
       main.setting->ready = controller_setting_ready_yes;
@@ -687,7 +691,7 @@ extern "C" {
       }
     }
 
-    for (;;) {
+    for (; main.thread->enabled; ) {
 
       entry_actions = &main.setting->entry.items.array[cache->ats.array[at_i]].actions;
 
@@ -930,7 +934,18 @@ extern "C" {
           break;
         }
         else if (entry_action->type == controller_entry_action_type_consider || entry_action->type == controller_entry_action_type_rule) {
-          f_thread_lock_write(&main.thread->lock.rule);
+
+          status = controller_lock_write(main.thread, &main.thread->lock.rule);
+
+          if (status == F_signal) {
+            break;
+          }
+
+          if (!main.thread->enabled) {
+            f_thread_unlock(&main.thread->lock.rule);
+
+            break;
+          }
 
           status = controller_rules_increase(&main.setting->rules);
 
@@ -971,6 +986,8 @@ extern "C" {
             }
           }
 
+          if (!main.thread->enabled) break;
+
           // the rule is not yet loaded, ensure that it is loaded.
           if (status != F_true) {
 
@@ -990,7 +1007,17 @@ extern "C" {
             memcpy(cache_name_item, cache->action.name_item.string, cache->action.name_item.used);
             memcpy(cache_name_file, cache->action.name_file.string, cache->action.name_file.used);
 
-            f_thread_lock_write(&main.thread->lock.rule);
+            status = controller_lock_write(main.thread, &main.thread->lock.rule);
+
+            if (status == F_signal) {
+              break;
+            }
+
+            if (!main.thread->enabled) {
+              f_thread_unlock(&main.thread->lock.rule);
+
+              break;
+            }
 
             status = controller_rule_read(alias_rule, main, cache, &main.setting->rules.array[main.setting->rules.used]);
 
@@ -1010,6 +1037,12 @@ extern "C" {
             cache->action.line_action = cache_line_action;
             cache->action.line_item = cache_line_item;
 
+            if (!main.thread->enabled) {
+              f_thread_unlock(&main.thread->lock.rule);
+
+              break;
+            }
+
             if (F_status_is_error(status)) {
 
               if (main.data->error.verbosity != f_console_verbosity_quiet) {
@@ -1039,7 +1072,18 @@ extern "C" {
               if (controller_find_process(alias_rule, main.thread->processs, 0) == F_false) {
 
                 f_thread_unlock(&main.thread->lock.process);
-                f_thread_lock_write(&main.thread->lock.process);
+
+                status = controller_lock_write(main.thread, &main.thread->lock.process);
+
+                if (status == F_signal) {
+                  break;
+                }
+
+                if (!main.thread->enabled) {
+                  f_thread_unlock(&main.thread->lock.process);
+
+                  break;
+                }
 
                 status = controller_processs_increase(&main.thread->processs);
 
@@ -1051,7 +1095,20 @@ extern "C" {
                   // only copy the rule alias, as that is all that is needed at this point (the entire rule gets copied prior to executing/processing).
                   controller_process_t *process = main.thread->processs.array[main.thread->processs.used];
 
-                  f_thread_lock_write(&process->lock);
+                  status = controller_lock_write(main.thread, &process->lock);
+
+                  if (status == F_signal) {
+                    f_thread_unlock(&main.thread->lock.process);
+
+                    break;
+                  }
+
+                  if (!main.thread->enabled) {
+                    f_thread_unlock(&process->lock);
+                    f_thread_unlock(&main.thread->lock.process);
+
+                    break;
+                  }
 
                   process->rule.alias.used = 0;
 
@@ -1108,7 +1165,7 @@ extern "C" {
 
             status = controller_rule_process_begin(process_options, alias_rule, controller_rule_action_type_start, rule_options, stack, main, *cache);
 
-            if (F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal) {
+            if (F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal || !main.thread->enabled) {
               break;
             }
           }
@@ -1236,6 +1293,10 @@ extern "C" {
       }
     } // for
 
+    if (!main.thread->enabled) {
+      return F_signal;
+    }
+
     if (status == F_child || status == F_signal) {
       return status;
     }
index f9679dc5cba3e540e6383a8a0b0a8ad125c9733d..764f090e11fa699a6ec6036d2631e3cc34617717 100644 (file)
@@ -839,12 +839,7 @@ extern "C" {
       return status;
     }
 
-    for (i = 0; i < process->rule.items.used; ++i) {
-
-      if (!main.thread->enabled) {
-        status = F_signal;
-        break;
-      }
+    for (i = 0; i < process->rule.items.used && main.thread->enabled; ++i) {
 
       if (process->rule.items.array[i].type == controller_rule_item_type_setting) continue;
 
@@ -872,7 +867,7 @@ extern "C" {
 
           status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, main, &execute_set, process);
 
-          if (status == F_child) break;
+          if (status == F_child || status == F_signal) break;
 
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
@@ -895,7 +890,7 @@ extern "C" {
 
           status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, main, &execute_set, process);
 
-          if (status == F_child) break;
+          if (status == F_child || status == F_signal) break;
 
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
@@ -916,7 +911,7 @@ extern "C" {
 
           status = controller_rule_execute_pid_with(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, main, &execute_set, process);
 
-          if (status == F_child) break;
+          if (status == F_child || status == F_signal) break;
 
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
@@ -956,6 +951,10 @@ extern "C" {
 
     f_macro_string_maps_t_delete_simple(environment);
 
+    if (!main.thread->enabled) {
+      return F_signal;
+    }
+
     if (status == F_child || status == F_signal || F_status_is_error(status)) {
       return status;
     }
@@ -1016,7 +1015,18 @@ extern "C" {
       result = 0;
 
       f_thread_unlock(&process->lock);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
 
       // assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
       process->child = id_child;
@@ -1027,8 +1037,25 @@ extern "C" {
       // have the parent wait for the child process to finish.
       waitpid(id_child, &result, 0);
 
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
+
       f_thread_unlock(&process->lock);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
 
       // remove the pid now that waidpid() has returned.
       process->child = 0;
@@ -1138,7 +1165,18 @@ extern "C" {
       result = 0;
 
       f_thread_unlock(&process->lock);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
 
       // assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
       process->child = id_process;
@@ -1149,8 +1187,25 @@ extern "C" {
       // have the parent wait for the child process to finish. @todo do not wait, this is a background execution!
       waitpid(id_process, &result, 0);
 
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
+
       f_thread_unlock(&process->lock);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        return F_signal;
+      }
 
       // remove the pid now that waidpid() has returned. @todo do not clear until forked execution is known to have exited, this is a background execution
       process->child = 0;
@@ -1840,8 +1895,12 @@ extern "C" {
           else {
             f_thread_unlock(&main.thread->lock.rule);
           }
+
+          if (!main.thread->enabled) break;
         } // for
 
+        if (!main.thread->enabled) break;
+
         if (status == F_child || status == F_signal) break;
 
         if (F_status_is_error(status) && !(process->options & controller_rule_option_simulate)) break;
@@ -1923,7 +1982,21 @@ extern "C" {
     f_array_length_t id_rule = 0;
 
     f_thread_unlock(&process->lock);
-    f_thread_lock_write(&process->lock);
+
+    status = controller_lock_write(main.thread, &process->lock);
+
+    if (status == F_signal) {
+      f_thread_lock_read(&process->lock);
+
+      return status;
+    }
+
+    if (!main.thread->enabled) {
+      f_thread_unlock(&process->lock);
+      f_thread_lock_read(&process->lock);
+
+      return F_signal;
+    }
 
     if (F_status_is_error(status)) {
       process->rule.status = controller_status_simplify_error(F_status_set_fine(status));
@@ -1932,7 +2005,22 @@ extern "C" {
       process->rule.status = status;
     }
 
-    f_thread_lock_write(&main.thread->lock.rule);
+    status = controller_lock_write(main.thread, &main.thread->lock.rule);
+
+    if (status == F_signal) {
+      f_thread_unlock(&process->lock);
+      f_thread_lock_read(&process->lock);
+
+      return status;
+    }
+
+    if (!main.thread->enabled) {
+      f_thread_unlock(&process->lock);
+      f_thread_unlock(&main.thread->lock.rule);
+      f_thread_lock_read(&process->lock);
+
+      return F_signal;
+    }
 
     status = controller_rule_find(process->rule.alias, main.setting->rules, &id_rule);
 
@@ -2006,7 +2094,21 @@ extern "C" {
       process = main.thread->processs.array[at];
 
       f_thread_lock_read(&process->active);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        f_thread_unlock(&process->active);
+
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+        f_thread_unlock(&process->active);
+
+        return F_signal;
+      }
 
       // once a write lock on the process is achieved, it is safe to unlock the main process read lock.
       f_thread_unlock(&main.thread->lock.process);
@@ -2029,7 +2131,21 @@ extern "C" {
     }
 
     f_thread_unlock(&process->lock);
-    f_thread_lock_write(&process->lock);
+
+    status = controller_lock_write(main.thread, &process->lock);
+
+    if (status == F_signal) {
+      f_thread_unlock(&process->active);
+
+      return status;
+    }
+
+    if (!main.thread->enabled) {
+      f_thread_unlock(&process->lock);
+      f_thread_unlock(&process->active);
+
+      return F_signal;
+    }
 
     process->state = controller_process_state_active;
     process->action = action;
@@ -2109,6 +2225,8 @@ extern "C" {
         status = controller_rule_process_do(process_options, process);
 
         if (status == F_child || status == F_signal) {
+          f_thread_unlock(&process->active);
+
           return status;
         }
       }
@@ -2127,15 +2245,21 @@ extern "C" {
 #ifndef _di_controller_rule_process_do_
   f_status_t controller_rule_process_do(const uint8_t options, controller_process_t *process) {
 
-    // the process lock shall be held for the duration of this processing (aside from switching between read to/from write).
-    if (options & controller_process_option_asynchronous) f_thread_lock_read(&process->active);
+    // the process and active locks shall be held for the duration of this processing (aside from switching between read to/from write).
+    if (options & controller_process_option_asynchronous) {
+      f_thread_lock_read(&process->active);
+    }
+
     f_thread_lock_read(&process->lock);
 
     controller_main_t main = controller_macro_main_t_initialize((controller_data_t *) process->main_data, (controller_setting_t *) process->main_setting, (controller_thread_t *) process->main_thread);
 
     if (!main.thread->enabled) {
       f_thread_unlock(&process->lock);
-      if (options & controller_process_option_asynchronous) f_thread_unlock(&process->active);
+
+      if (options & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
+      }
 
       return F_signal;
     }
@@ -2150,7 +2274,26 @@ extern "C" {
     if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
 
       f_thread_unlock(&process->lock);
-      f_thread_lock_write(&process->lock);
+
+      status = controller_lock_write(main.thread, &process->lock);
+
+      if (status == F_signal) {
+        if (options & controller_process_option_asynchronous) {
+          f_thread_unlock(&process->active);
+        }
+
+        return status;
+      }
+
+      if (!main.thread->enabled) {
+        f_thread_unlock(&process->lock);
+
+        if (options & controller_process_option_asynchronous) {
+          f_thread_unlock(&process->active);
+        }
+
+        return F_signal;
+      }
 
       controller_rule_delete_simple(&process->rule);
 
@@ -2188,6 +2331,16 @@ extern "C" {
           }
         } // for
 
+        if (!main.thread->enabled) {
+          f_thread_unlock(&process->lock);
+
+          if (options & controller_process_option_asynchronous) {
+            f_thread_unlock(&process->active);
+          }
+
+          return F_signal;
+        }
+
         if (F_status_is_error_not(status)) {
           status = f_type_array_lengths_increase(&process->stack);
 
@@ -2196,7 +2349,26 @@ extern "C" {
           }
           else {
             f_thread_unlock(&process->lock);
-            f_thread_lock_write(&process->lock);
+
+            status = controller_lock_write(main.thread, &process->lock);
+
+            if (status == F_signal) {
+              if (options & controller_process_option_asynchronous) {
+                f_thread_unlock(&process->active);
+              }
+
+              return status;
+            }
+
+            if (!main.thread->enabled) {
+              f_thread_unlock(&process->lock);
+
+              if (options & controller_process_option_asynchronous) {
+                f_thread_unlock(&process->active);
+              }
+
+              return F_signal;
+            }
 
             process->stack.array[process->stack.used++] = id_rule;
 
@@ -2208,7 +2380,26 @@ extern "C" {
 
       if (F_status_is_error(status)) {
         f_thread_unlock(&main.thread->lock.rule);
-        f_thread_lock_write(&main.thread->lock.rule);
+
+        status = controller_lock_write(main.thread, &main.thread->lock.rule);
+
+        if (status == F_signal) {
+          if (options & controller_process_option_asynchronous) {
+            f_thread_unlock(&process->active);
+          }
+
+          return status;
+        }
+
+        if (!main.thread->enabled) {
+          f_thread_unlock(&main.thread->lock.rule);
+
+          if (options & controller_process_option_asynchronous) {
+            f_thread_unlock(&process->active);
+          }
+
+          return F_signal;
+        }
 
         if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
           main.setting->rules.array[id_rule].status = status;
@@ -2225,9 +2416,27 @@ extern "C" {
     else {
       f_thread_unlock(&main.thread->lock.rule);
 
-      status = F_status_set_error(F_found_not);
+      status = controller_lock_write(main.thread, &main.thread->lock.rule);
+
+      if (status == F_signal) {
+        if (options & controller_process_option_asynchronous) {
+          f_thread_unlock(&process->active);
+        }
+
+        return status;
+      }
 
-      f_thread_lock_write(&main.thread->lock.rule);
+      if (!main.thread->enabled) {
+        f_thread_unlock(&main.thread->lock.rule);
+
+        if (options & controller_process_option_asynchronous) {
+          f_thread_unlock(&process->active);
+        }
+
+        return F_signal;
+      }
+
+      status = F_status_set_error(F_found_not);
 
       if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
         main.setting->rules.array[id_rule].status = status;
@@ -2245,12 +2454,39 @@ extern "C" {
       }
     }
 
-    if (status == F_child || status == F_signal) {
+    if (status == F_child) {
       return status;
     }
 
     f_thread_unlock(&process->lock);
-    f_thread_lock_write(&process->lock);
+
+    if (status == F_signal) {
+      if (options & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
+      }
+
+      return status;
+    }
+
+    status = controller_lock_write(main.thread, &process->lock);
+
+    if (status == F_signal) {
+      if (options & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
+      }
+
+      return status;
+    }
+
+    if (!main.thread->enabled) {
+      f_thread_unlock(&process->lock);
+
+      if (options & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
+      }
+
+      return F_signal;
+    }
 
     if ((options & controller_process_option_execute) && (options & controller_process_option_asynchronous)) {
       process->state = controller_process_state_done;
@@ -2262,7 +2498,10 @@ extern "C" {
     process->stack.used = used_original_stack;
 
     f_thread_unlock(&process->lock);
-    if (options & controller_process_option_asynchronous) f_thread_unlock(&process->active);
+
+    if (options & controller_process_option_asynchronous) {
+      f_thread_unlock(&process->active);
+    }
 
     if (main.thread->enabled) {
       if (options & controller_process_option_execute) {
@@ -4629,9 +4868,12 @@ extern "C" {
 
     if (!main.thread->processs.used) {
       f_thread_unlock(&main.thread->lock.process);
+
       return;
     }
 
+    f_status_t status = F_none;
+
     f_array_length_t i = 0;
     f_array_length_t j = 0;
 
@@ -4668,7 +4910,21 @@ extern "C" {
 
         if (process->state == controller_process_state_done) {
           f_thread_unlock(&process->lock);
-          f_thread_lock_write(&process->lock);
+
+          status = controller_lock_write(main.thread, &process->lock);
+
+          if (status == F_signal) {
+            f_thread_unlock(&process->active);
+
+            break;
+          }
+
+          if (!main.thread->enabled) {
+            f_thread_unlock(&process->lock);
+            f_thread_unlock(&process->active);
+
+            break;
+          }
 
           if (process->state == controller_process_state_done) {
             f_thread_unlock(&process->active);
index fb0b5bf13c3d00e77a0cf6823846280b82719e9e..bb23ee32583110776c5f936a604ab6901f7a9f77 100644 (file)
@@ -12,12 +12,16 @@ extern "C" {
 #ifndef _di_controller_thread_cleanup_
   void * controller_thread_cleanup(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     const controller_main_t *main = (controller_main_t *) arguments;
 
     if (!main->thread->enabled) return 0;
 
     const unsigned int interval = main->data->parameters[controller_parameter_test].result == f_console_result_found ? controller_thread_cleanup_interval_short : controller_thread_cleanup_interval_long;
 
+    f_status_t status = F_none;
+
     while (main->thread->enabled) {
 
       sleep(interval);
@@ -65,7 +69,20 @@ extern "C" {
               break;
             }
 
-            f_thread_lock_write(&process->lock);
+            status = controller_lock_write(main->thread, &process->lock);
+
+            if (status == F_signal) {
+              f_thread_unlock(&process->active);
+
+              break;
+            }
+
+            if (!main->thread->enabled) {
+              f_thread_unlock(&process->lock);
+              f_thread_unlock(&process->active);
+
+              break;
+            }
 
             process->state = controller_process_state_idle;
             process->id_thread = 0;
@@ -96,6 +113,8 @@ extern "C" {
 #ifndef _di_controller_thread_control_
   void * controller_thread_control(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     controller_main_t *main = (controller_main_t *) arguments;
 
     if (!main->thread->enabled) return 0;
@@ -288,6 +307,8 @@ extern "C" {
 #ifndef _di_controller_thread_process_
   void * controller_thread_process(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     controller_process_t *process = (controller_process_t *) arguments;
 
     {
@@ -411,33 +432,20 @@ extern "C" {
           nanosleep(&wait, 0);
         }
 
-        f_thread_signal(process->id_thread, F_signal_kill);
-
         f_thread_join(process->id_thread, 0);
 
         process->child = 0;
         process->id_thread = 0;
       }
     } // for
-
-    // guarantee these threads are terminated.
-    if (main->thread->id_cleanup) {
-      f_thread_signal(main->thread->id_cleanup, F_signal_kill);
-    }
-
-    if (main->thread->id_control) {
-      f_thread_signal(main->thread->id_control, F_signal_kill);
-    }
-
-    if (main->thread->id_rule) {
-      f_thread_signal(main->thread->id_rule, F_signal_kill);
-    }
   }
 #endif // _di_controller_thread_process_cancel_
 
 #ifndef _di_controller_thread_entry_
   void * controller_thread_entry(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
 
     if (!entry->main->thread->enabled) return 0;
@@ -508,6 +516,8 @@ extern "C" {
 #ifndef _di_controller_thread_rule_
   void * controller_thread_rule(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     controller_main_t *main = (controller_main_t *) arguments;
 
     if (!main->thread->enabled) return 0;