Update some of the comments.
The "consider" was not being loaded into the "process" array.
Change the behavior to allow for loading the "consider" rules into the "process" array without executing them.
Fix a bug where some entry items were being skipped.
The counter was being added an extra time during the loop, resulting in the skip.
The child processes are not exiting cleanly.
The state F_child was not being fully bubbled up the call stack.
It seems that when forking within a thread, the forked child process, on exit will not result in the thread returning to the caller.
For now there is 1 block (according to valgrind) that is "possibly lost" for every forked child process.
A more in depth investigation is needed.
Add a stub for an asynchronous entry that I will be writing for additional testing of the asynchronous processing.
* main_thread: Used for passing the controller_thread_t data to the process thread (to populate controller_main_t).
*/
#ifndef _di_controller_process_t_
+ #define controller_process_option_asynchronous 0x1
+ #define controller_process_option_execute 0x2
+
enum {
controller_process_state_idle = 1,
controller_process_state_busy,
/**
* A wrapper used for passing a common set of all data, particularly for sharing between threads.
*
- * data: All standard program data.
- * setting: All loaded settings.
- * processs: All Rule Process data.
- * thread: All thread related data.
+ * data: All standard program data.
+ * setting: All loaded settings.
+ * thread: All thread related data.
*/
#ifndef _di_controller_main_t_
typedef struct {
/**
* Fully deallocate all memory for the given process without caring about return status.
*
- * This does not close/cancel the id_thread.
- * Be sure to properly close and/or detach the thread.
- *
* @param process
* The process to deallocate.
*
/**
* Fully deallocate all memory for the given processs without caring about return status.
*
- * This does not close/cancel the id_thread for any process.
- * Be sure to properly close and/or detach the thread for each process.
- *
* @param processs
* The process array to deallocate.
*
f_array_length_t at_j = 1;
uint8_t rule_options = 0;
+ uint8_t process_options = 0;
controller_entry_action_t *entry_action = 0;
controller_entry_actions_t *entry_actions = 0;
}
}
- if (F_status_is_error_not(status) && entry_action->type == controller_entry_action_type_rule) {
+ 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;
+ }
+
if (simulate) {
rule_options |= controller_rule_option_simulate;
}
}
if (entry_action->code & controller_entry_rule_code_asynchronous) {
+ process_options |= controller_process_option_asynchronous;
rule_options |= controller_rule_option_asynchronous;
}
- status = controller_rule_process_begin(entry_action->code & controller_entry_rule_code_asynchronous, alias_rule, controller_rule_action_type_start, rule_options, stack, main, *cache);
+ 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) {
break;
break;
}
}
- else {
- cache->ats.array[at_j]++;
- }
} // for
if (status == F_child || status == F_signal) {
const f_string_static_t alias_other = f_macro_string_static_t_initialize(alias_other_buffer, main.setting->rules.array[id_rule].alias.used);
- status = controller_rule_process_begin(F_false, alias_other, action, process->options & controller_rule_option_asynchronous ? process->options - controller_rule_option_asynchronous : process->options, process->stack, main, process->cache);
+ status = controller_rule_process_begin(controller_process_option_execute, alias_other, action, process->options & controller_rule_option_asynchronous ? process->options - controller_rule_option_asynchronous : process->options, process->stack, main, process->cache);
if (status == F_child || status == F_signal) {
- f_thread_unlock(&process_other->active);
-
break;
}
#endif // _di_controller_rule_process_
#ifndef _di_controller_rule_process_begin_
- f_status_t controller_rule_process_begin(const bool asynchronous, 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 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) {
if (!main.thread->enabled) {
return F_signal;
f_thread_unlock(&process->lock);
if (F_status_is_error_not(status)) {
- if (asynchronous) {
+ if ((process_options & controller_process_option_execute) && (process_options & controller_process_option_asynchronous)) {
status = f_thread_create(0, &process->id_thread, controller_thread_process, (void *) process);
if (F_status_is_error(status)) {
}
}
else {
- status = controller_rule_process_do(F_false, process);
+ status = controller_rule_process_do(process_options, process);
+
+ if (status == F_child || status == F_signal) {
+ return status;
+ }
}
}
#endif // _di_controller_rule_process_begin_
#ifndef _di_controller_rule_process_do_
- f_status_t controller_rule_process_do(const bool asynchronous, controller_process_t *process) {
+ 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 (asynchronous) f_thread_lock_read(&process->active);
+ 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 (asynchronous) f_thread_unlock(&process->active);
+ if (options & controller_process_option_asynchronous) f_thread_unlock(&process->active);
return F_signal;
}
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 {
+ else if (options & controller_process_option_execute) {
for (f_array_length_t i = 0; i < process->stack.used && main.thread->enabled; ++i) {
if (process->stack.array[i] == id_rule) {
f_thread_unlock(&main.thread->lock.rule);
}
- else {
+ else if (options & controller_process_option_execute) {
if (main.thread->enabled) {
status = controller_rule_process(controller_rule_action_type_start, main, process);
}
}
}
+ if (status == F_child || status == F_signal) {
+ return status;
+ }
+
f_thread_unlock(&process->lock);
f_thread_lock_write(&process->lock);
- if (asynchronous) {
+ if ((options & controller_process_option_execute) && (options & controller_process_option_asynchronous)) {
process->state = controller_process_state_done;
}
else {
process->stack.used = used_original_stack;
f_thread_unlock(&process->lock);
- if (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) {
- // inform all things waiting that the process has finished running.
- f_thread_mutex_lock(&process->wait_lock);
- f_thread_condition_signal_all(&process->wait);
- f_thread_mutex_unlock(&process->wait_lock);
+ // inform all things waiting that the process has finished running.
+ f_thread_mutex_lock(&process->wait_lock);
+ f_thread_condition_signal_all(&process->wait);
+ f_thread_mutex_unlock(&process->wait_lock);
+ }
}
else {
return F_signal;
/**
* Synchronously or Asynchronously begin processing some rule.
*
- * @param asynchronous
- * If TRUE, then asynchronously execute a process.
- * If FALSE, then synchronously execute a process.
+ * @param process_options
+ * If controller_process_option_asynchronous, then asynchronously execute.
+ * If controller_process_option_execute, then load and 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
* @see f_thread_create()
*/
#ifndef _di_controller_rule_process_begin_
- extern f_status_t controller_rule_process_begin(const bool asynchronous, 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 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;
#endif // _di_controller_rule_process_begin_
/**
* 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 asynchronous
- * If TRUE, designates that this function is being asynchronously executed.
- * If FALSE, designates that this function is being synchronously executed.
+ * @param options
+ * If controller_process_option_asynchronous, then asynchronously execute.
+ * If controller_process_option_execute, then load and 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.
*
* @see controller_rule_process_begin()
*/
#ifndef _di_controller_rule_process_do_
- extern f_status_t controller_rule_process_do(const bool asynchronous, controller_process_t *process) f_gcc_attribute_visibility_internal;
+ extern f_status_t controller_rule_process_do(const uint8_t options, controller_process_t *process) f_gcc_attribute_visibility_internal;
#endif // _di_controller_rule_process_do_
/**
const controller_main_t *main = (controller_main_t *) arguments;
- if (!main->thread->enabled) f_thread_exit(0);
+ 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_thread_unlock(&process->lock);
+ // close any still open thread.
if (process->id_thread) {
f_thread_join(process->id_thread, 0);
f_thread_unlock(&process->active);
} // for
- if (main->thread->processs.size) {
- f_array_length_t j = main->thread->processs.size;
-
- for (i = main->thread->processs.size - 1; j && main->thread->enabled; --i, --j) {
-
- if (!main->thread->processs.array[i]) continue;
-
- process = main->thread->processs.array[i];
-
- // if "active" has a read lock, then do not attempt to clean it.
- if (f_thread_lock_write_try(&process->active) != F_none) {
- break;
- }
-
- // if "lock" has a read or write lock, then do not attempt to clean it.
- if (f_thread_lock_write_try(&process->lock) != F_none) {
- f_thread_unlock(&process->active);
-
- break;
- }
-
- // if process is active or busy, then do not attempt to clean it.
- if (process->state == controller_process_state_active || process->state == controller_process_state_busy) {
- f_thread_unlock(&process->active);
- f_thread_unlock(&process->lock);
-
- break;
- }
-
- f_thread_unlock(&process->lock);
-
- if (process->id_thread) {
- f_thread_join(process->id_thread, 0);
-
- if (!main->thread->enabled) {
- f_thread_unlock(&process->active);
-
- break;
- }
-
- f_thread_lock_write(&process->lock);
-
- process->state = controller_process_state_idle;
- process->id_thread = 0;
-
- f_thread_unlock(&process->lock);
- }
-
- // deallocate dynamic portions of the structure that are only ever needed while the rule is being processed.
- controller_cache_delete_simple(&process->cache);
- f_type_array_lengths_resize(0, &process->stack);
-
- --main->thread->processs.used;
-
- f_thread_unlock(&process->active);
- } // for
- }
-
f_thread_unlock(&main->thread->lock.process);
}
} // while
- f_thread_exit(0);
+ return 0;
}
#endif // _di_controller_thread_cleanup_
controller_main_t *main = (controller_main_t *) arguments;
- if (!main->thread->enabled) f_thread_exit(0);
+ if (!main->thread->enabled) return 0;
- f_thread_exit(0);
+ return 0;
}
#endif // _di_controller_thread_control_
f_thread_signal(main->thread->id_rule, F_signal_kill);
}
- f_thread_exit(0);
+ return 0;
}
#endif // _di_controller_thread_exit_force_
// wait for the entry thread to complete before starting the rule thread.
f_thread_join(thread.id_rule, 0);
+ if (thread.status == F_child) {
+ controller_thread_delete_simple(&thread);
+
+ return F_child;
+ }
+
thread.id_rule = 0;
if (thread.enabled) {
{
controller_thread_t *thread = (controller_thread_t *) process->main_thread;
- if (!thread->enabled) f_thread_exit(0);
+ if (!thread->enabled) return 0;
}
- controller_rule_process_do(F_true, process);
+ if (controller_rule_process_do(controller_process_option_asynchronous | controller_process_option_execute, process) == 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.
+ // Deallocate as much as possible.
- f_thread_exit(0);
+ controller_data_t *data = (controller_data_t *) process->main_data;
+ controller_setting_t *setting = (controller_setting_t *) process->main_setting;
+ controller_thread_t *thread = (controller_thread_t *) process->main_thread;
+
+ controller_thread_delete_simple(thread);
+ controller_setting_delete_simple(setting);
+ controller_delete_data(data);
+ }
+
+ return 0;
}
#endif // _di_controller_thread_process_
controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
- if (!entry->main->thread->enabled) f_thread_exit(0);
+ if (!entry->main->thread->enabled) return 0;
controller_data_t *data = entry->main->data;
controller_cache_t *cache = &entry->main->thread->cache;
}
}
- f_thread_exit(0);
+ 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.
+ // Deallocate as much as possible.
+
+ controller_thread_delete_simple(entry->main->thread);
+ controller_setting_delete_simple(entry->main->setting);
+ controller_delete_data(entry->main->data);
+ }
+
+ return 0;
}
#endif // _di_controller_thread_entry_
controller_main_t *main = (controller_main_t *) arguments;
- if (!main->thread->enabled) f_thread_exit(0);
+ if (!main->thread->enabled) return 0;
- f_thread_exit(0);
+ return 0;
}
#endif // _di_controller_thread_rule_
controller_main_t *main = (controller_main_t *) arguments;
- if (!main->thread->enabled) f_thread_exit(0);
+ if (!main->thread->enabled) return 0;
for (int signal = 0; main->thread->enabled; ) {
}
} // for
- f_thread_exit(0);
+ return 0;
}
#endif // _di_controller_thread_signal_
--- /dev/null
+# fss-0005
+
+main: