]> Kevux Git Server - fll/commitdiff
Update: handle asynchronous failures for failsafe, update locks, and related fixes.
authorKevin Day <thekevinday@gmail.com>
Wed, 14 Apr 2021 17:08:27 +0000 (12:08 -0500)
committerKevin Day <thekevinday@gmail.com>
Wed, 14 Apr 2021 17:08:27 +0000 (12:08 -0500)
The failsafe needs to be triggered when a required but asynchronous process fails.
I originally planned on implementing this via locks but I would rather avoid adding even more locks.
This approach instead provides a wait loop at the end of the entry waiting only on all required processes.
If any of these fail, then the wait will return the requirement failure.
This is a change to the wait all function and behavior, which is updating to now return statuses.

Get rid of process->status, it is no longer needed now that process->rule.status exists.
Having it remain is wasteful and confusing.

The entry processing is updated to be failsafe aware.
This now potentially operates the failsafe rule item.
The failsafe must not do a wait all like normal operation inside of the entry processing function.

There are some discrepancies between "process options" and "rule options".
Technically, there are no "options" on a rule as this is a concept introduced for/by entries.
The process uses these options but most of them were named "rule options".
I then later created a "process options" to add override so that even if the rule is requested asynchronous, if a dependant thread is requiring it, then it will instead run synchronously in the thread of the depending process.
This resulted in both "process options" and "rule options".
They really are the same, so instead remove the current "process options" and rename all "rule options" into "process options".
There are now no longer any "rule options".
I then renamed the "process options" variables to "force options".

Remove signal_all from delete process.
The signals should now all be timed and will exit when thread disabled is set.

The execution used to be designed with the intent that is where the asynchronous processing would handle.
After multiple iterations in design, this is no longer the case.
Update the code to always pass parameter option fl_execute_parameter_option_return instead of doing it only for asynchronous processes.

Add missing re-locks.
Some of the functions require that a certain (read) lock be held prior to calling the function.
When the function returns, the expected lock should still be held.
It so happens that this is not consistently the case.
Certain error or exit states are returning without re-establishing the expected (read) locks.

The controller_rule_find() returns a boolean and not a status with potential error bits.
Fix a block of code where it is checking for error bit on the return value of this function.

The controller_rule_read() also returns a boolean and is being handled as a status with a bit.
In this case, change it to return a status given that this make more sense in this particular case.

Remove relevant/related stale code and add missing comments.

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-rule.h
level_3/controller/c/private-thread.c
level_3/controller/documents/entry.txt

index b7aa086691e8339ea2d46a0aab7a7bb0a1218d96..870a5648e0f8d7f2b80d3195007ed45b142bd1a7 100644 (file)
@@ -220,7 +220,6 @@ extern "C" {
       process->id_thread = 0;
     }
 
-    f_thread_condition_signal_all(&process->wait);
     f_thread_condition_delete(&process->wait);
 
     controller_lock_delete_rw(&process->lock);
@@ -273,7 +272,7 @@ extern "C" {
 
       f_thread_lock_read(&process->lock);
 
-      if (process->status != F_known_not || !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
+      if (process->rule.status != F_known_not || !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
         f_thread_unlock(&process->lock);
 
         return F_none;
@@ -367,7 +366,6 @@ extern "C" {
           return status;
         }
         else {
-          process->status = F_known_not;
           process->rule.status = F_known_not;
         }
       } // for
index f90a0ef2d888ba0a2b597edd1126704a18cbdaeb..b7969354db567e64af12bab6e745fbee36ef7d16 100644 (file)
@@ -635,12 +635,6 @@ extern "C" {
     controller_rule_setting_type_wish,
   };
 
-  #define controller_rule_option_asynchronous 0x1
-  #define controller_rule_option_require      0x2
-  #define controller_rule_option_simulate     0x4
-  #define controller_rule_option_validate     0x8
-  #define controller_rule_option_wait         0x10
-
   // bitwise codes representing properties on controller_rule_t that have been found in the rule file.
   #define controller_rule_has_control_group 0x1
   #define controller_rule_has_group         0x2
@@ -771,7 +765,10 @@ extern "C" {
  */
 #ifndef _di_controller_process_t_
   #define controller_process_option_asynchronous 0x1
-  #define controller_process_option_execute      0x2
+  #define controller_process_option_require      0x2
+  #define controller_process_option_simulate     0x4
+  #define controller_process_option_validate     0x8
+  #define controller_process_option_wait         0x10
 
   enum {
     controller_process_state_idle = 1,
@@ -783,8 +780,6 @@ extern "C" {
   typedef struct {
     f_array_length_t id;
 
-    f_status_t status;
-
     uint8_t state;
     uint8_t action;
     uint8_t options;
@@ -808,7 +803,6 @@ extern "C" {
 
   #define controller_process_t_initialize { \
     0, \
-    F_known_not, \
     0, \
     0, \
     0, \
@@ -1093,7 +1087,6 @@ extern "C" {
 
   typedef struct {
     bool enabled;
-    bool failed;
     int signal;
     f_status_t status;
 
@@ -1109,7 +1102,6 @@ extern "C" {
 
   #define controller_thread_t_initialize { \
     F_true, \
-    F_false, \
     0, \
     F_none, \
     f_thread_id_t_initialize, \
@@ -1366,6 +1358,7 @@ extern "C" {
  *
  * @return
  *   F_none on success.
+ *   F_signal on (exit) signal received, lock will not be set when this is returned.
  *   F_status if main thread is disabled and write lock was never achieved.
  *
  *   Status from: f_thread_lock_write_timed().
index e9d4fcab2fb90525ccfc6e50fd8c488573bc356c..abf6e800d46bdb11bddaf81d651b5f2e124e9204 100644 (file)
@@ -623,12 +623,11 @@ extern "C" {
     f_array_length_t i = 0;
     f_array_length_t j = 0;
 
-    f_array_length_t at = 0;
     f_array_length_t at_i = 0;
     f_array_length_t at_j = 1;
 
-    uint8_t rule_options = 0;
-    uint8_t process_options = 0;
+    uint8_t options_force = 0;
+    uint8_t options_process = 0;
 
     controller_entry_action_t *entry_action = 0;
     controller_entry_actions_t *entry_actions = 0;
@@ -865,8 +864,7 @@ extern "C" {
         }
         else if (entry_action->type == controller_entry_action_type_item) {
 
-          // @todo also prevent failsafe item from being recursively called, when failsafe == F_true.
-          if (entry_action->number == 0 || entry_action->number >= main->setting->entry.items.used) {
+          if (entry_action->number == 0 || entry_action->number >= main->setting->entry.items.used || failsafe && entry_action->number == main->setting->failsafe_item_id) {
 
             // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
             if (main->data->error.verbosity != f_console_verbosity_quiet) {
@@ -968,7 +966,7 @@ extern "C" {
 
           f_thread_lock_read(&main->thread->lock.rule);
 
-          status = controller_rule_find(alias_rule, main->setting->rules, &at);
+          status = controller_rule_find(alias_rule, main->setting->rules, 0);
 
           f_thread_unlock(&main->thread->lock.rule);
 
@@ -1069,7 +1067,6 @@ extern "C" {
               f_thread_lock_read(&main->thread->lock.process);
 
               if (controller_find_process(alias_rule, main->thread->processs, 0) == F_false) {
-
                 f_thread_unlock(&main->thread->lock.process);
 
                 status = controller_lock_write(main->thread, &main->thread->lock.process);
@@ -1136,37 +1133,33 @@ extern "C" {
           }
 
           if (F_status_is_error_not(status)) {
-            process_options = 0;
-            rule_options = 0;
-
-            if (entry_action->type == controller_entry_action_type_rule) {
-              process_options |= controller_process_option_execute;
-            }
+            options_force = 0;
+            options_process = 0;
 
             if (simulate) {
-              rule_options |= controller_rule_option_simulate;
+              options_process |= controller_process_option_simulate;
             }
 
             if (entry_action->code & controller_entry_rule_code_require) {
-              rule_options |= controller_rule_option_require;
+              options_process |= controller_process_option_require;
             }
 
             if (entry_action->code & controller_entry_rule_code_wait) {
-              rule_options |= controller_rule_option_wait;
+              options_process |= controller_process_option_wait;
             }
 
             if (main->data->parameters[controller_parameter_validate].result == f_console_result_found) {
-              rule_options |= controller_rule_option_validate;
+              options_process |= controller_process_option_validate;
             }
 
             if (entry_action->code & controller_entry_rule_code_asynchronous) {
               if (main->data->parameters[controller_parameter_validate].result != f_console_result_found) {
-                process_options |= controller_process_option_asynchronous;
-                rule_options |= controller_rule_option_asynchronous;
+                options_force |= controller_process_option_asynchronous;
+                options_process |= controller_process_option_asynchronous;
               }
             }
 
-            status = controller_rule_process_begin(process_options, alias_rule, controller_rule_action_type_start, rule_options, stack, *main, *cache);
+            status = controller_rule_process_begin(options_force, alias_rule, controller_rule_action_type_start, options_process, stack, *main, *cache);
 
             if (F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal || !main->thread->enabled) {
               break;
@@ -1322,9 +1315,18 @@ extern "C" {
       return status;
     }
 
-    // @todo wait for all asynchronous processes to complete.
-    //       then, check to see if any "required" rule failed.
-    //       if failed, then return F_status_set_error(F_require).
+    // check to see if any requied processes failed, but do not do this if already operating in failsafe.
+    if (F_status_is_error_not(status) && !failsafe) {
+      const f_status_t status_wait = controller_rule_wait_all(*main, F_true, 0);
+
+      if (status_wait == F_signal) {
+        return F_signal;
+      }
+
+      if (F_status_set_fine(status_wait) == F_require) {
+        return F_status_set_error(F_require);
+      }
+    }
 
     if (simulate) {
       if (main->data->error.verbosity != f_console_verbosity_quiet) {
index 70e6bb2c749f415dcfe4afe16966476535bd2110..70ed1e62ff05530aad86c24bd408a8e86ebae654 100644 (file)
@@ -776,7 +776,7 @@ extern "C" {
       execute_set.as.control_group = &process->rule.control_group;
 
       // make sure all required cgroup directories exist.
-      if (process->status == F_known_not) {
+      if (process->rule.status == F_known_not) {
         status = fll_control_group_prepare(process->rule.control_group);
 
         if (F_status_is_error(status)) {
@@ -833,11 +833,7 @@ extern "C" {
         if (process->rule.items.array[i].actions.array[j].type != action) continue;
 
         execute_set.parameter.data = 0;
-        execute_set.parameter.option = fl_execute_parameter_option_threadsafe;
-
-        if (options & controller_rule_option_asynchronous) {
-          execute_set.parameter.option |= fl_execute_parameter_option_return;
-        }
+        execute_set.parameter.option = fl_execute_parameter_option_threadsafe | fl_execute_parameter_option_return;
 
         if (process->rule.items.array[i].type == controller_rule_item_type_command) {
 
@@ -852,7 +848,7 @@ extern "C" {
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
-            if (!(options & controller_rule_option_simulate)) break;
+            if (!(options & controller_process_option_simulate)) break;
 
             success = F_failure;
           }
@@ -875,7 +871,7 @@ extern "C" {
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
-            if (!(options & controller_rule_option_simulate)) break;
+            if (!(options & controller_process_option_simulate)) break;
 
             success = F_status_set_error(F_failure);
           }
@@ -896,7 +892,7 @@ extern "C" {
           if (F_status_is_error(status)) {
             process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
 
-            if (!(options & controller_rule_option_simulate)) break;
+            if (!(options & controller_process_option_simulate)) break;
 
             success = F_failure;
           }
@@ -924,7 +920,7 @@ extern "C" {
         }
       } // for
 
-      if (status == F_child || status == F_signal || F_status_is_error(status) && !(options & controller_rule_option_simulate)){
+      if (status == F_child || status == F_signal || F_status_is_error(status) && !(options & controller_process_option_simulate)) {
          break;
        }
     } // for
@@ -960,7 +956,7 @@ extern "C" {
     int result = 0;
     pid_t id_child = 0;
 
-    if (options & controller_rule_option_simulate) {
+    if (options & controller_process_option_simulate) {
       if (main.data->error.verbosity != f_console_verbosity_quiet) {
         f_thread_mutex_lock(&main.thread->lock.print);
 
@@ -1001,11 +997,14 @@ extern "C" {
       status_lock = controller_lock_write(main.thread, &process->lock);
 
       if (status_lock == F_signal) {
+        f_thread_lock_read(&process->lock);
+
         return status_lock;
       }
 
       if (!main.thread->enabled) {
         f_thread_unlock(&process->lock);
+        f_thread_lock_read(&process->lock);
 
         return F_signal;
       }
@@ -1019,12 +1018,12 @@ extern "C" {
       // have the parent wait for the child process to finish.
       waitpid(id_child, &result, 0);
 
-      f_thread_unlock(&process->lock);
-
       if (!main.thread->enabled) {
         return F_signal;
       }
 
+      f_thread_unlock(&process->lock);
+
       status_lock = controller_lock_write(main.thread, &process->lock);
 
       if (status_lock == F_signal) {
@@ -1033,6 +1032,7 @@ extern "C" {
 
       if (!main.thread->enabled) {
         f_thread_unlock(&process->lock);
+        f_thread_lock_read(&process->lock);
 
         return F_signal;
       }
@@ -1113,7 +1113,7 @@ extern "C" {
     //       otherwise this needs to call an asynchronous execute process.
     //       until then, this controller_rule_execute_pid_with() function is not correct and only represents a process that forks to the background.
 
-    if (options & controller_rule_option_simulate) {
+    if (options & controller_process_option_simulate) {
 
       if (main.data->error.verbosity != f_console_verbosity_quiet) {
 
@@ -1683,7 +1683,7 @@ extern "C" {
       return status;
     }
 
-    if ((process->options & controller_rule_option_simulate) && main.data->parameters[controller_parameter_validate].result == f_console_result_found) {
+    if ((process->options & controller_process_option_simulate) && main.data->parameters[controller_parameter_validate].result == f_console_result_found) {
       controller_rule_validate(process->rule, controller_rule_action_type_start, process->options, main, &process->cache);
     }
 
@@ -1700,7 +1700,7 @@ extern "C" {
 
       controller_process_t *process_other = 0;
 
-      uint8_t rule_options = 0;
+      uint8_t options_process = 0;
 
       f_string_dynamics_t * const dynamics[] = {
         &process->rule.need,
@@ -1716,9 +1716,9 @@ extern "C" {
 
       // i==0 is need, i==1 is want, i==2 is wish.
       // loop through all dependencies: wait for depedency, execute dependency, fail due to missing required dependency, or skip unrequired missing dependencies.
-      for (i = 0; i < 3; ++i) {
+      for (i = 0; i < 3 && main.thread->enabled; ++i) {
 
-        for (j = 0; j < dynamics[i]->used; ++j) {
+        for (j = 0; j < dynamics[i]->used && main.thread->enabled; ++j) {
 
           process_other = 0;
           found = F_false;
@@ -1728,29 +1728,26 @@ extern "C" {
           status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_process);
 
           if (status == F_true) {
+            found = F_true;
+
             process_other = main.thread->processs.array[id_process];
 
             f_thread_lock_read(&process_other->active);
-          }
-
-          f_thread_unlock(&main.thread->lock.process);
-
-          if (status == F_true) {
+            f_thread_unlock(&main.thread->lock.process);
             f_thread_lock_read(&main.thread->lock.rule);
 
             status = controller_rule_find(dynamics[i]->array[j], main.setting->rules, &id_rule);
 
             f_thread_unlock(&main.thread->lock.rule);
-
-            found = F_true;
+          }
+          else {
+            f_thread_unlock(&main.thread->lock.process);
           }
 
           if (status != F_true) {
             found = F_false;
             id_rule = 0;
 
-            f_thread_lock_read(&main.thread->lock.rule);
-
             if (i == 0) {
               f_thread_mutex_lock(&main.thread->lock.print);
 
@@ -1761,9 +1758,7 @@ extern "C" {
 
               status = F_status_set_error(F_found_not);
 
-              if (!(process->options & controller_rule_option_simulate)) {
-                f_thread_unlock(&main.thread->lock.rule);
-
+              if (!(process->options & controller_process_option_simulate)) {
                 if (process_other) {
                   f_thread_unlock(&process_other->active);
                 }
@@ -1782,11 +1777,8 @@ extern "C" {
               }
             }
           }
-          else {
+          else if (found) {
             f_thread_lock_read(&main.thread->lock.rule);
-          }
-
-          if (found) {
 
             // the process_other may have write locks, which needs to be avoided, so copy the alias from the rule.
             char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1];
@@ -1810,6 +1802,8 @@ extern "C" {
               f_thread_unlock(&process_other->lock);
 
               controller_process_wait(main, process_other);
+
+              status = process_other->rule.status;
             }
             else {
               f_thread_lock_read(&main.thread->lock.rule);
@@ -1818,18 +1812,18 @@ extern "C" {
                 f_thread_unlock(&main.thread->lock.rule);
                 f_thread_unlock(&process_other->lock);
 
-                rule_options = controller_process_option_execute;
+                options_process = 0;
 
                 if (main.data->parameters[controller_parameter_test].result == f_console_result_found) {
-                  rule_options |= controller_rule_option_simulate;
+                  options_process |= controller_process_option_simulate;
                 }
 
                 if (main.data->parameters[controller_parameter_validate].result == f_console_result_found) {
-                  rule_options |= controller_rule_option_validate;
+                  options_process |= controller_process_option_validate;
                 }
 
                 // synchronously execute dependency.
-                status = controller_rule_process_begin(controller_process_option_execute, alias_other, controller_rule_action_type_start, rule_options, process->stack, main, process_other->cache);
+                status = controller_rule_process_begin(0, alias_other, controller_rule_action_type_start, options_process, process->stack, main, process_other->cache);
 
                 if (status == F_child || status == F_signal) {
                   f_thread_unlock(&process_other->active);
@@ -1846,7 +1840,7 @@ extern "C" {
 
                     controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
 
-                    if (!(process_other->options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) {
+                    if (!(process_other->options & controller_process_option_simulate) || F_status_set_fine(status) == F_memory_not) {
                       f_thread_unlock(&process_other->active);
 
                       break;
@@ -1865,6 +1859,8 @@ extern "C" {
                 }
               }
               else {
+                status = main.setting->rules.array[id_rule].status;
+
                 f_thread_unlock(&main.thread->lock.rule);
                 f_thread_unlock(&process_other->lock);
               }
@@ -1879,6 +1875,8 @@ extern "C" {
             f_thread_lock_read(&main.thread->lock.rule);
 
             if (F_status_is_error(main.setting->rules.array[id_rule].status)) {
+              f_thread_unlock(&main.thread->lock.rule);
+
               if (i == 0 || i == 1) {
                 f_thread_mutex_lock(&main.thread->lock.print);
 
@@ -1889,12 +1887,8 @@ extern "C" {
 
                 controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
 
-                if (!(process_other->options & controller_rule_option_simulate)) {
-                  f_thread_unlock(&main.thread->lock.rule);
-
-                  if (process_other) {
-                    f_thread_unlock(&process_other->active);
-                  }
+                if (!(process_other->options & controller_process_option_simulate)) {
+                  f_thread_unlock(&process_other->active);
 
                   break;
                 }
@@ -1910,25 +1904,19 @@ extern "C" {
                 }
               }
             }
-
-            f_thread_unlock(&main.thread->lock.rule);
-          }
-          else {
-            f_thread_unlock(&main.thread->lock.rule);
+            else {
+              f_thread_unlock(&main.thread->lock.rule);
+            }
           }
 
           if (process_other) {
             f_thread_unlock(&process_other->active);
           }
-
-          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;
+        if (F_status_is_error(status) && !(process->options & controller_process_option_simulate)) break;
       } // for
     }
 
@@ -1940,18 +1928,18 @@ extern "C" {
       return F_signal;
     }
 
-    if ((process->options & controller_rule_option_wait) && F_status_is_error_not(status)) {
-      controller_rule_wait_all(main, process); // @fixme review this, it needs to check anything depending on this!
+    if ((process->options & controller_process_option_wait) && F_status_is_error_not(status)) {
+      controller_rule_wait_all(main, F_false, process);
 
       if (!main.thread->enabled) {
         return F_signal;
       }
     }
 
-    if (!(process->options & controller_rule_option_validate) && F_status_is_error_not(status)) {
+    if (!(process->options & controller_process_option_validate) && F_status_is_error_not(status)) {
 
       // find at least one of the requested action when the rule is required.
-      if (process->options & controller_rule_option_require) {
+      if (process->options & controller_process_option_require) {
         bool missing = F_true;
 
         f_array_length_t j = 0;
@@ -2036,7 +2024,7 @@ extern "C" {
       f_thread_unlock(&process->lock);
       f_thread_lock_read(&process->lock);
 
-      return status_lock;
+      return F_signal;
     }
 
     if (!main.thread->enabled) {
@@ -2047,12 +2035,7 @@ extern "C" {
       return F_signal;
     }
 
-    status = controller_rule_find(process->rule.alias, main.setting->rules, &id_rule);
-
-    if (F_status_is_error(status)) {
-      controller_rule_error_print(main.data->error, process->cache.action, F_status_set_fine(status), "controller_rule_find", F_true, F_true, main.thread);
-    }
-    else {
+    if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
       controller_rule_t *rule = &main.setting->rules.array[id_rule];
 
       rule->status = process->rule.status;
@@ -2083,7 +2066,7 @@ extern "C" {
 #endif // _di_controller_rule_process_
 
 #ifndef _di_controller_rule_process_begin_
-  f_status_t controller_rule_process_begin(const uint8_t process_options, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) {
+  f_status_t controller_rule_process_begin(const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) {
 
     if (!main.thread->enabled) {
       return F_signal;
@@ -2124,6 +2107,7 @@ extern "C" {
 
       if (status == F_signal) {
         f_thread_unlock(&process->active);
+        f_thread_unlock(&main.thread->lock.process);
 
         return status;
       }
@@ -2131,6 +2115,7 @@ extern "C" {
       if (!main.thread->enabled) {
         f_thread_unlock(&process->lock);
         f_thread_unlock(&process->active);
+        f_thread_unlock(&main.thread->lock.process);
 
         return F_signal;
       }
@@ -2146,9 +2131,8 @@ extern "C" {
         return F_busy;
       }
 
-      // the thread is done, so detach/close the thread.
+      // the thread is done, so close the thread.
       if (process->state == controller_process_state_done) {
-        f_thread_cancel(process->id_thread);
         f_thread_join(process->id_thread, 0);
       }
 
@@ -2239,7 +2223,7 @@ extern "C" {
     f_thread_unlock(&process->lock);
 
     if (F_status_is_error_not(status)) {
-      if ((process_options & controller_process_option_execute) && (process_options & controller_process_option_asynchronous)) {
+      if (options_force & controller_process_option_asynchronous) {
         status = f_thread_create(0, &process->id_thread, controller_thread_process, (void *) process);
 
         if (F_status_is_error(status)) {
@@ -2247,7 +2231,7 @@ extern "C" {
         }
       }
       else {
-        status = controller_rule_process_do(process_options, process);
+        status = controller_rule_process_do(options_force, process);
 
         if (status == F_child || status == F_signal) {
           f_thread_unlock(&process->active);
@@ -2268,10 +2252,10 @@ extern "C" {
 #endif // _di_controller_rule_process_begin_
 
 #ifndef _di_controller_rule_process_do_
-  f_status_t controller_rule_process_do(const uint8_t options, controller_process_t *process) {
+  f_status_t controller_rule_process_do(const uint8_t options_force, controller_process_t *process) {
 
     // 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) {
+    if (options_force & controller_process_option_asynchronous) {
       f_thread_lock_read(&process->active);
     }
 
@@ -2282,7 +2266,7 @@ extern "C" {
     if (!main.thread->enabled) {
       f_thread_unlock(&process->lock);
 
-      if (options & controller_process_option_asynchronous) {
+      if (options_force & controller_process_option_asynchronous) {
         f_thread_unlock(&process->active);
       }
 
@@ -2299,13 +2283,14 @@ extern "C" {
     f_thread_lock_read(&main.thread->lock.rule);
 
     if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
-
       f_thread_unlock(&process->lock);
 
       status_lock = controller_lock_write(main.thread, &process->lock);
 
       if (status_lock == F_signal) {
-        if (options & controller_process_option_asynchronous) {
+        f_thread_unlock(&main.thread->lock.rule);
+
+        if (options_force & controller_process_option_asynchronous) {
           f_thread_unlock(&process->active);
         }
 
@@ -2313,9 +2298,10 @@ extern "C" {
       }
 
       if (!main.thread->enabled) {
+        f_thread_unlock(&main.thread->lock.rule);
         f_thread_unlock(&process->lock);
 
-        if (options & controller_process_option_asynchronous) {
+        if (options_force & controller_process_option_asynchronous) {
           f_thread_unlock(&process->active);
         }
 
@@ -2328,13 +2314,12 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
       f_thread_lock_read(&process->lock);
-
       f_thread_unlock(&main.thread->lock.rule);
 
       if (F_status_is_error(status)) {
         controller_entry_error_print(main.data->error, process->cache.action, F_status_set_fine(status), "controller_rule_copy", F_true, main.thread);
       }
-      else if (options & controller_process_option_execute) {
+      else {
         for (f_array_length_t i = 0; i < process->stack.used && main.thread->enabled; ++i) {
 
           if (process->stack.array[i] == id_rule) {
@@ -2361,7 +2346,7 @@ extern "C" {
         if (!main.thread->enabled) {
           f_thread_unlock(&process->lock);
 
-          if (options & controller_process_option_asynchronous) {
+          if (options_force & controller_process_option_asynchronous) {
             f_thread_unlock(&process->active);
           }
 
@@ -2380,7 +2365,7 @@ extern "C" {
             status_lock = controller_lock_write(main.thread, &process->lock);
 
             if (status_lock == F_signal) {
-              if (options & controller_process_option_asynchronous) {
+              if (options_force & controller_process_option_asynchronous) {
                 f_thread_unlock(&process->active);
               }
 
@@ -2390,7 +2375,7 @@ extern "C" {
             if (!main.thread->enabled) {
               f_thread_unlock(&process->lock);
 
-              if (options & controller_process_option_asynchronous) {
+              if (options_force & controller_process_option_asynchronous) {
                 f_thread_unlock(&process->active);
               }
 
@@ -2405,90 +2390,67 @@ extern "C" {
         }
       }
 
-      if (F_status_is_error(status)) {
-        f_thread_unlock(&main.thread->lock.rule);
-
-        status_lock = controller_lock_write(main.thread, &main.thread->lock.rule);
-
-        if (status_lock == F_signal) {
-          if (options & controller_process_option_asynchronous) {
-            f_thread_unlock(&process->active);
-          }
-
-          return status_lock;
-        }
-
-        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;
-        }
-
-        f_thread_unlock(&main.thread->lock.rule);
-      }
-      else if (options & controller_process_option_execute) {
-        if (main.thread->enabled) {
-          status = controller_rule_process(controller_rule_action_type_start, main, process);
-        }
+      if (F_status_is_error_not(status)) {
+        status = controller_rule_process(controller_rule_action_type_start, main, process);
       }
     }
     else {
       f_thread_unlock(&main.thread->lock.rule);
 
-      status_lock = controller_lock_write(main.thread, &main.thread->lock.rule);
+      status = F_status_set_error(F_found_not);
 
-      if (status_lock == F_signal) {
-        if (options & controller_process_option_asynchronous) {
-          f_thread_unlock(&process->active);
-        }
+      if (main.data->error.verbosity != f_console_verbosity_quiet) {
+        f_thread_mutex_lock(&main.thread->lock.print);
 
-        return status_lock;
-      }
+        controller_rule_item_error_print_rule_not_loaded(main.data->error, process->rule.alias.string);
+        controller_rule_error_print_cache(main.data->error, process->cache.action, F_false);
 
-      if (!main.thread->enabled) {
-        f_thread_unlock(&main.thread->lock.rule);
+        controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
+      }
+    }
 
-        if (options & controller_process_option_asynchronous) {
-          f_thread_unlock(&process->active);
-        }
+    if (status == F_child) {
+      f_thread_unlock(&process->lock);
 
-        return F_signal;
+      if (options_force & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
       }
 
-      status = F_status_set_error(F_found_not);
+      return status;
+    }
 
-      if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
-        main.setting->rules.array[id_rule].status = status;
-      }
+    status_lock = controller_lock_write(main.thread, &main.thread->lock.rule);
 
-      f_thread_unlock(&main.thread->lock.rule);
+    if (status_lock == F_signal) {
+      f_thread_unlock(&process->lock);
 
-      if (main.data->error.verbosity != f_console_verbosity_quiet) {
-        f_thread_mutex_lock(&main.thread->lock.print);
+      if (options_force & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
+      }
 
-        controller_rule_item_error_print_rule_not_loaded(main.data->error, process->rule.alias.string);
-        controller_rule_error_print_cache(main.data->error, process->cache.action, F_false);
+      return status_lock;
+    }
 
-        controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
+    if (!main.thread->enabled) {
+      f_thread_unlock(&main.thread->lock.rule);
+      f_thread_unlock(&process->lock);
+
+      if (options_force & controller_process_option_asynchronous) {
+        f_thread_unlock(&process->active);
       }
+
+      return F_signal;
     }
 
-    if (status == F_child) {
-      return status;
+    if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
+      main.setting->rules.array[id_rule].status = status;
     }
 
+    f_thread_unlock(&main.thread->lock.rule);
     f_thread_unlock(&process->lock);
 
     if (status == F_signal) {
-      if (options & controller_process_option_asynchronous) {
+      if (options_force & controller_process_option_asynchronous) {
         f_thread_unlock(&process->active);
       }
 
@@ -2498,7 +2460,7 @@ extern "C" {
     status_lock = controller_lock_write(main.thread, &process->lock);
 
     if (status_lock == F_signal) {
-      if (options & controller_process_option_asynchronous) {
+      if (options_force & controller_process_option_asynchronous) {
         f_thread_unlock(&process->active);
       }
 
@@ -2508,14 +2470,14 @@ extern "C" {
     if (!main.thread->enabled) {
       f_thread_unlock(&process->lock);
 
-      if (options & controller_process_option_asynchronous) {
+      if (options_force & controller_process_option_asynchronous) {
         f_thread_unlock(&process->active);
       }
 
       return F_signal;
     }
 
-    if ((options & controller_process_option_execute) && (options & controller_process_option_asynchronous)) {
+    if (options_force & controller_process_option_asynchronous) {
       process->state = controller_process_state_done;
     }
     else {
@@ -2531,7 +2493,7 @@ extern "C" {
 
     f_thread_unlock(&process->lock);
 
-    if (options & controller_process_option_asynchronous) {
+    if (options_force & controller_process_option_asynchronous) {
       f_thread_unlock(&process->active);
     }
 
@@ -2787,10 +2749,10 @@ extern "C" {
       controller_rule_item_error_print(main.data->error, cache->action, for_item, main.thread);
 
       rule->status = controller_status_simplify_error(F_status_set_fine(status));
-      return F_false;
+      return rule->status;
     }
 
-    return F_true;
+    return F_none;
   }
 #endif // _di_controller_rule_read_
 
@@ -4640,9 +4602,9 @@ extern "C" {
         fprintf(data->output.stream, "' has no '");
         fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, controller_rule_action_type_name(action).string, data->context.set.important.after->string);
         fprintf(data->output.stream, "' action to execute and would '");
-        fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, options & controller_rule_option_require ? controller_string_fail_s : controller_string_succeed_s, data->context.set.important.after->string);
+        fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, options & controller_process_option_require ? controller_string_fail_s : controller_string_succeed_s, data->context.set.important.after->string);
         fprintf(data->output.stream, "' because it is '");
-        fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, options & controller_rule_option_require ? controller_string_required_s : controller_string_optional_s, data->context.set.important.after->string);
+        fprintf(data->output.stream, "%s%s%s", data->context.set.important.before->string, options & controller_process_option_require ? controller_string_required_s : controller_string_optional_s, data->context.set.important.after->string);
         fprintf(data->output.stream, "'.%c", f_string_eol_s[0]);
       }
     }
@@ -4650,8 +4612,8 @@ extern "C" {
     fprintf(data->output.stream, "%c", f_string_eol_s[0]);
     fprintf(data->output.stream, "Rule %s%s%s {%c", data->context.set.title.before->string, rule.alias.used ? rule.alias.string : f_string_empty_s, data->context.set.title.after->string, f_string_eol_s[0]);
     fprintf(data->output.stream, "  %s%s%s %s%c", data->context.set.important.before->string, controller_string_name_s, data->context.set.important.after->string, rule.name.used ? rule.name.string : f_string_empty_s, f_string_eol_s[0]);
-    fprintf(data->output.stream, "  %s%s%s %s%c", data->context.set.important.before->string, controller_string_how_s, data->context.set.important.after->string, options & controller_rule_option_asynchronous ? controller_string_asynchronous : controller_string_synchronous_s, f_string_eol_s[0]);
-    fprintf(data->output.stream, "  %s%s%s %s%c", data->context.set.important.before->string, controller_string_wait_s, data->context.set.important.after->string, options & controller_rule_option_wait ? controller_string_yes : controller_string_no_s, f_string_eol_s[0]);
+    fprintf(data->output.stream, "  %s%s%s %s%c", data->context.set.important.before->string, controller_string_how_s, data->context.set.important.after->string, options & controller_process_option_asynchronous ? controller_string_asynchronous : controller_string_synchronous_s, f_string_eol_s[0]);
+    fprintf(data->output.stream, "  %s%s%s %s%c", data->context.set.important.before->string, controller_string_wait_s, data->context.set.important.after->string, options & controller_process_option_wait ? controller_string_yes : controller_string_no_s, f_string_eol_s[0]);
 
     if (f_capability_supported()) {
       fprintf(data->output.stream, "  %s%s%s ", data->context.set.important.before->string, controller_string_capability_s, data->context.set.important.after->string);
@@ -4880,17 +4842,18 @@ extern "C" {
 #endif // _di_controller_rule_validate_
 
 #ifndef _di_controller_rule_wait_all_
-  void controller_rule_wait_all(const controller_main_t main, controller_process_t *caller) {
+  f_status_t controller_rule_wait_all(const controller_main_t main, const bool required, controller_process_t *caller) {
 
     f_thread_lock_read(&main.thread->lock.process);
 
     if (!main.thread->processs.used) {
       f_thread_unlock(&main.thread->lock.process);
 
-      return;
+      return F_data_not;
     }
 
     f_status_t status = F_none;
+    bool required_not_run = F_false;
 
     f_array_length_t i = 0;
     f_array_length_t j = 0;
@@ -4899,13 +4862,16 @@ extern "C" {
 
     bool skip = F_false;
 
-    for (; i < main.thread->processs.used && main.thread->enabled; ++i, skip = F_false) {
+    for (; i < main.thread->processs.used && main.thread->enabled; ++i) {
 
       process = main.thread->processs.array[i];
 
       if (caller) {
+        f_thread_unlock(&main.thread->lock.process);
         f_thread_lock_read(&main.thread->lock.rule);
 
+        skip = F_false;
+
         for (j = 0; j < caller->stack.used && main.thread->enabled; ++j) {
 
           if (main.thread->processs.array[caller->stack.array[j]] && fl_string_dynamic_compare(process->rule.alias, main.thread->processs.array[caller->stack.array[j]]->rule.alias) == F_equal_to) {
@@ -4916,6 +4882,7 @@ extern "C" {
         } // for
 
         f_thread_unlock(&main.thread->lock.rule);
+        f_thread_lock_read(&main.thread->lock.process);
 
         if (!main.thread->enabled) break;
         if (skip) continue;
@@ -4925,6 +4892,16 @@ extern "C" {
       f_thread_unlock(&main.thread->lock.process);
       f_thread_lock_read(&process->lock);
 
+      if (required) {
+        if (!(process->options & controller_process_option_require)) {
+          f_thread_unlock(&process->lock);
+          f_thread_unlock(&process->active);
+          f_thread_lock_read(&main.thread->lock.process);
+
+          continue;
+        }
+      }
+
       if (!process->state || process->state == controller_process_state_idle || process->state == controller_process_state_done) {
 
         if (process->state == controller_process_state_done) {
@@ -4934,6 +4911,7 @@ extern "C" {
 
           if (status == F_signal) {
             f_thread_unlock(&process->active);
+            f_thread_lock_read(&main.thread->lock.process);
 
             break;
           }
@@ -4941,17 +4919,42 @@ extern "C" {
           if (!main.thread->enabled) {
             f_thread_unlock(&process->lock);
             f_thread_unlock(&process->active);
+            f_thread_lock_read(&main.thread->lock.process);
 
             break;
           }
 
           if (process->state == controller_process_state_done) {
+            f_thread_unlock(&process->active);
+
             if (f_thread_lock_write_try(&process->active) == F_none) {
               f_thread_join(process->id_thread, 0);
 
               process->state = controller_process_state_idle;
               process->id_thread = 0;
+
+              f_thread_unlock(&process->active);
             }
+
+            f_thread_lock_read(&process->active);
+          }
+
+          f_thread_unlock(&process->lock);
+          f_thread_lock_read(&process->lock);
+        }
+
+        if (required && (process->options & controller_process_option_require)) {
+          if (F_status_is_error(process->rule.status)) {
+            status = F_status_set_error(F_require);
+
+            f_thread_unlock(&process->lock);
+            f_thread_unlock(&process->active);
+            f_thread_lock_read(&main.thread->lock.process);
+
+            break;
+          }
+          else if (process->rule.status == F_known_not) {
+            required_not_run = F_true;
           }
         }
 
@@ -4959,13 +4962,42 @@ extern "C" {
         f_thread_unlock(&process->active);
         f_thread_lock_read(&main.thread->lock.process);
 
+        if (F_status_set_fine(status) == F_require) break;
+
         continue;
       }
 
       if (process->state == controller_process_state_active || process->state == controller_process_state_busy) {
         f_thread_unlock(&process->lock);
 
-        controller_process_wait(main, process);
+        status = controller_process_wait(main, process);
+
+        if (status == F_signal) {
+          f_thread_unlock(&process->active);
+          f_thread_lock_read(&main.thread->lock.process);
+
+          break;
+        }
+
+        if (required) {
+          f_thread_lock_read(&process->lock);
+
+          if ((process->options & controller_process_option_require)) {
+            f_thread_unlock(&process->lock);
+
+            if (F_status_is_error(process->rule.status)) {
+              status = F_status_set_error(F_require);
+
+              f_thread_unlock(&process->active);
+              f_thread_lock_read(&main.thread->lock.process);
+
+              break;
+            }
+          }
+          else {
+            f_thread_unlock(&process->lock);
+          }
+        }
       }
       else {
         f_thread_unlock(&process->lock);
@@ -4973,9 +5005,21 @@ extern "C" {
 
       f_thread_unlock(&process->active);
       f_thread_lock_read(&main.thread->lock.process);
+
+      if (status == F_signal || F_status_set_fine(status) == F_require) break;
     } // for
 
     f_thread_unlock(&main.thread->lock.process);
+
+    if (status == F_signal || F_status_set_fine(status) == F_require) {
+      return status;
+    }
+
+    if (required_not_run) {
+      return F_require;
+    }
+
+    return F_none;
   }
 #endif // _di_controller_rule_wait_all_
 
index 506250989ecbd16cb88e44175d277fb690c98594..698e664e10f990c98286a381728ecd256c454c6c 100644 (file)
@@ -338,7 +338,7 @@ extern "C" {
  *   - controller_rule_action_type_stop
  * @param options
  *   A number using bits to represent specific boolean options.
- *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_process_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  * @param main
  *   The main data.
  * @param process
@@ -379,7 +379,7 @@ extern "C" {
  *   The arguments to pass to the program.
  * @param options
  *   A number using bits to represent specific boolean options.
- *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_process_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  * @param main
  *   The main data.
  * @param execute_set
@@ -425,7 +425,7 @@ extern "C" {
  *   The arguments to pass to the program.
  * @param options
  *   A number using bits to represent specific boolean options.
- *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_process_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
  * @param main
  *   The main data.
  * @param execute_set
@@ -590,20 +590,19 @@ extern "C" {
 #endif // _di_controller_rule_process_
 
 /**
- * Synchronously or Asynchronously begin processing some rule.
+ * Synchronously or asynchronously begin processing some rule.
  *
- * @param process_options
- *   If controller_process_option_asynchronous, then asynchronously execute.
- *   If controller_process_option_execute, then load and execute.
+ * @param options_force
+ *   Force the given process options, only supporting a subset of process options.
  *
+ *   If controller_process_option_asynchronous, then asynchronously execute.
  *   If not controller_process_option_asynchronous, then synchronously execute.
- *   If not controller_process_option_execute, then load only, do not execute at all.
  * @param alias_rule
  *   The alias of the rule, such as "boot/init".
  * @param action
  *   The action to perform based on the action type codes.
  * @param options
- *   A number using bits to represent specific boolean options.
+ *   The process options to pass to the process.
  * @param stack
  *   A stack representing the processes already running in this rule process dependency tree.
  *   This is used to prevent circular dependencies.
@@ -631,7 +630,7 @@ extern "C" {
  * @see f_thread_create()
  */
 #ifndef _di_controller_rule_process_begin_
-  extern f_status_t controller_rule_process_begin(const uint8_t process_options, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_process_begin(const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_process_begin_
 
 /**
@@ -640,12 +639,11 @@ 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 process structure.
  *
- * @param options
- *   If controller_process_option_asynchronous, then asynchronously execute.
- *   If controller_process_option_execute, then load and execute.
+ * @param options_force
+ *   Force the given process options, only supporting a subset of process options.
  *
+ *   If controller_process_option_asynchronous, then asynchronously execute.
  *   If not controller_process_option_asynchronous, then synchronously execute.
- *   If not controller_process_option_execute, then load only, do not execute at all.
  * @param process
  *   The process data.
  *
@@ -666,7 +664,7 @@ extern "C" {
  * @see controller_rule_process_begin()
  */
 #ifndef _di_controller_rule_process_do_
-  extern f_status_t controller_rule_process_do(const uint8_t options, controller_process_t *process) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_process_do(const uint8_t options_force, controller_process_t *process) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_process_do_
 
 /**
@@ -685,8 +683,9 @@ extern "C" {
  *   The rule status will be updated by this function.
  *
  * @return
- *   F_true on success.
- *   F_false on failure.
+ *   F_none on success.
+ *
+ *   Simplified status (with error bit) from controller_status_simplify_error() on failure.
  *
  * @see controller_rule_items_increase_by().
  * @see controller_rule_item_read().
@@ -763,8 +762,8 @@ extern "C" {
  * @param options
  *   A number using bits to represent specific boolean options.
  *   If no bits set, then operate normally in a synchronous manner.
- *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
- *   If bit controller_rule_option_asynchronous, then run asynchronously.
+ *   If bit controller_process_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_process_option_asynchronous, then run asynchronously.
  * @param main
  *   The main data.
  * @param cache
@@ -779,13 +778,24 @@ extern "C" {
  *
  * @param main
  *   The main data.
+ * @param required
+ *   If TRUE, then only process required rules and if a required rule has failed, return.
+ *   If FALSE, process all waits, returning normally.
  * @param caller
  *   The process representing the caller so that the process never waits on itself.
  *   (optional) set to 0 when calling from a thread that is not running/executing any process.
  *   Failure to set this to the process on a thread running/executing a process will likely result in a deadlock.
+ *
+ * @return
+ *    F_none on success.
+ *    F_data_not on success and nothing to do.
+ *    F_require on success, but a required rule has not been run yet.
+ *    F_signal on (exit) signal received.
+ *
+ *    F_require (with error bit set) if a required process is in failed status when required is TRUE.
  */
 #ifndef _di_controller_rule_wait_all_
-  extern void controller_rule_wait_all(const controller_main_t main, controller_process_t *process) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_wait_all(const controller_main_t main, const bool required, controller_process_t *process) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_wait_all_
 
 #ifdef __cplusplus
index 349faf911723ef19ba9cb1814d94ba5c49a6ea21..e7b769ff5a01660b2bbfd3c1dede57edcb04dfa2 100644 (file)
@@ -191,16 +191,16 @@ extern "C" {
           }
         }
         else {
-          f_thread_join(thread.id_rule, 0);
-
-          thread.id_rule = 0;
-          status = thread.status;
-
           if (status == F_child) {
             controller_thread_delete_simple(&thread);
 
             return F_child;
           }
+
+          f_thread_join(thread.id_rule, 0);
+
+          thread.id_rule = 0;
+          status = thread.status;
         }
       }
     }
@@ -315,7 +315,9 @@ extern "C" {
       if (!thread->enabled) return 0;
     }
 
-    if (controller_rule_process_do(controller_process_option_asynchronous | controller_process_option_execute, process) == F_child) {
+    const f_status_t status = controller_rule_process_do(controller_process_option_asynchronous, process);
+
+    if (status == F_child) {
 
       // A forked child process should deallocate memory on exit.
       // It seems that this function doesn't return to the calling thread for a forked child process, even with the "return 0;" below.
index 88ba0381a8927fa3348567307c64a3a5877f6a82..038b4fd5e0d949fed590373393c7cc36f5da4107 100644 (file)
@@ -21,6 +21,7 @@ Entry Documentation:
   The "failsafe" Action accepts only a valid Item Name in which will be executed when a failure is detected.
   Only a single "failsafe" Action may exist at a time.
   Each successive "failsafe" Action specified replaces the previously defined "failsafe" Action (in a top-down manner).
+  When operating in "failsafe", the "require" Action is ignored given that it is meaningless once operating in failsafe.
 
   The "item" Action accepts only a valid Item Name in which will be immediately executed.
   Any valid Item Name, except for the reserved "main", may be used.