build_sources_library main/instance.c main/path.c
build_sources_library main/rule.c main/rule/action.c main/rule/execute.c main/rule/expand.c main/rule/instance.c main/rule/is.c main/rule/item.c main/rule/parameter.c main/rule/read.c main/rule/setting.c main/rule/validate.c main/rule/wait.c
build_sources_library main/print/data.c main/print/debug.c main/print/error.c main/print/lock.c main/print/message.c main/print/rule.c main/print/verbose.c main/print/warning.c
-build_sources_library main/signal.c main/time.c main/thread.c main/thread/instance.c main/thread/is.c
+build_sources_library main/signal.c main/time.c
+build_sources_library main/thread.c main/thread/control.c main/thread/entry.c main/thread/instance.c main/thread/is.c main/thread/rule.c
build_sources_headers main/common.h main/controller.h main/common/define.h main/common/enumeration.h main/common/print.h main/common/string.h main/common/thread.h main/common/type.h
build_sources_headers main/common/define/control.h main/common/define/entry.h main/common/define/rule.h main/common/define/thread.h
build_sources_headers main/instance.h main/path.h
build_sources_headers main/rule.h main/rule/action.h main/rule/execute.h main/rule/expand.h main/rule/instance.h main/rule/is.h main/rule/item.h main/rule/parameter.h main/rule/read.h main/rule/setting.h main/rule/validate.h main/rule/wait.h
build_sources_headers main/print/data.h main/print/debug.h main/print/error.h main/print/lock.h main/print/message.h main/print/rule.h main/print/verbose.h main/print/warning.h
-build_sources_headers main/signal.h main/time.h main/thread.h main/thread/instance.h main/thread/is.h
+build_sources_headers main/signal.h main/time.h
+build_sources_headers main/thread.h main/thread/control.h main/thread/entry.h main/thread/instance.h main/thread/is.h main/thread/rule.h
build_sources_documentation man
#ifndef _di_controller_thread_d_
#define controller_thread_cleanup_interval_long_d 3600 // 1 hour in seconds.
#define controller_thread_cleanup_interval_short_d 180 // 3 minutes in seconds.
- #define controller_thread_exit_timeout_d 500 // 0.5 seconds in milliseconds.
- #define controller_thread_exit_process_cancel_wait_d 600000000 // 0.6 seconds in nanoseconds.
- #define controller_thread_exit_process_cancel_total_d 150 // 90 seconds in multiples of wait.
+ #define controller_main_thread_exit_timeout_d 500 // 0.5 seconds in milliseconds.
+ #define controller_main_thread_exit_process_cancel_wait_d 600000000 // 0.6 seconds in nanoseconds.
+ #define controller_main_thread_exit_process_cancel_total_d 150 // 90 seconds in multiples of wait.
#define controller_thread_simulation_timeout_d 200 // 0.2 seconds in milliseconds.
- #define controller_thread_signal_wait_timeout_seconds_d 70
- #define controller_thread_signal_wait_timeout_nanoseconds_d 0
+ #define controller_main_thread_signal_wait_timeout_seconds_d 70
+ #define controller_main_thread_signal_wait_timeout_nanoseconds_d 0
#define controller_thread_lock_read_timeout_seconds_d 3
#define controller_thread_lock_read_timeout_nanoseconds_d 0
#define controller_thread_wait_timeout_4_seconds_d 20
#define controller_thread_wait_timeout_4_nanoseconds_d 0
- #define controller_thread_exit_helper_timeout_seconds_d 0
- #define controller_thread_exit_helper_timeout_nanoseconds_d 100000000 // 0.1 seconds in nanoseconds.
+ #define controller_main_thread_exit_helper_timeout_seconds_d 0
+ #define controller_main_thread_exit_helper_timeout_nanoseconds_d 100000000 // 0.1 seconds in nanoseconds.
- #define controller_thread_exit_ready_timeout_seconds_d 0
- #define controller_thread_exit_ready_timeout_nanoseconds_d 500000000 // 0.5 seconds in nanoseconds.
+ #define controller_main_thread_exit_ready_timeout_seconds_d 0
+ #define controller_main_thread_exit_ready_timeout_nanoseconds_d 500000000 // 0.5 seconds in nanoseconds.
#endif // _di_controller_thread_d_
#ifdef __cplusplus
0, \
0, \
0, \
- controller_thread_exit_timeout_d, \
+ controller_main_thread_exit_timeout_d, \
0, \
0, \
0, \
// FLL-1 includes.
#include <fll/level_1/conversion.h>
#include <fll/level_1/execute.h>
+#include <fll/level_1/fss.h>
+#include <fll/level_1/fss/extended.h>
+#include <fll/level_1/fss/extended_list.h>
#include <fll/level_1/path.h>
#include <fll/level_1/print.h>
// FLL-2 includes.
#include <fll/level_2/error.h>
+#include <fll/level_2/fss.h>
+#include <fll/level_2/fss/basic_list.h>
+#include <fll/level_2/fss/extended.h>
+#include <fll/level_2/fss/extended_list.h>
+#include <fll/level_2/fss/payload.h>
#include <fll/level_2/print.h>
#include <fll/level_2/program.h>
#include <program/controller/main/print/warning.h>
#include <program/controller/main/signal.h>
#include <program/controller/main/time.h>
+#include <program/controller/main/thread/control.h>
+#include <program/controller/main/thread/entry.h>
#include <program/controller/main/thread/is.h>
#include <program/controller/main/thread/instance.h>
+#include <program/controller/main/thread/rule.h>
#include <program/controller/main/thread.h>
#include <program/controller/main/instance.h>
#include <program/controller/main/rule.h>
f_status_t status = F_okay;
controller_interrupt_t custom = macro_controller_interrupt_t_initialize_1(is_normal, global->thread);
- f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_main_thread_signal_state_fss, 0, (void *) &custom, 0);
f_number_unsigned_t i = 0;
if (actions->array[actions->used].parameters.array[0].used) {
state.step_large = controller_common_allocation_iki_large_d;
state.step_small = controller_common_allocation_iki_small_d;
- state.interrupt = &controller_thread_signal_state_iki;
+ state.interrupt = &controller_main_thread_signal_state_iki;
f_range_t range_iki = macro_f_range_t_initialize_2(actions->array[actions->used].parameters.array[0].used);
if (actions->array[actions->used].parameters.array[0].used) {
state.step_large = controller_common_allocation_iki_large_d;
state.step_small = controller_common_allocation_iki_small_d;
- state.interrupt = &controller_thread_signal_state_iki;
+ state.interrupt = &controller_main_thread_signal_state_iki;
f_range_t range_iki = macro_f_range_t_initialize_2(actions->array[actions->used].parameters.array[0].used);
if (F_status_is_error_not(status)) {
if (instance->action && (options_force & controller_instance_option_asynchronous_d)) {
if (instance->type == controller_instance_type_exit_e) {
- status = f_thread_create(0, &instance->id_thread, controller_thread_instance_other, (void *) instance);
+ status = f_thread_create(0, &instance->id_thread, controller_main_thread_instance_other, (void *) instance);
}
else {
- status = f_thread_create(0, &instance->id_thread, controller_thread_instance_normal, (void *) instance);
+ status = f_thread_create(0, &instance->id_thread, controller_main_thread_instance_normal, (void *) instance);
}
if (F_status_is_error(status)) {
f_status_t status = F_okay;
controller_interrupt_t custom = macro_controller_interrupt_t_initialize_1(is_normal, global->thread);
- f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_main_thread_signal_state_fss, 0, (void *) &custom, 0);
f_range_t range = macro_f_range_t_initialize_2(cache->buffer_item.used);
f_number_unsigned_t last = 0;
}
status = F_status_set_error(F_support_not);
+
break;
}
if (cache->buffer_file.used) {
controller_interrupt_t custom = macro_controller_interrupt_t_initialize_1(is_normal, global->thread);
- f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_main_thread_signal_state_fss, 0, (void *) &custom, 0);
f_range_t range = macro_f_range_t_initialize_2(cache->buffer_file.used);
fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments, &state);
f_range_t range2 = f_range_t_initialize;
controller_interrupt_t custom = macro_controller_interrupt_t_initialize_1(is_normal, global->thread);
- f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_state_t state = macro_f_state_t_initialize_1(controller_allocation_large_d, controller_allocation_small_d, F_okay, 0, 0, 0, &controller_main_thread_signal_state_fss, 0, (void *) &custom, 0);
fll_fss_extended_read(cache->buffer_item, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0, &state);
if (F_status_is_error(status)) {
controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
break;
}
if (F_status_set_fine(status) == F_memory_not) {
status_return = status;
+
break;
}
for (i = 0; i < instance_total; ++i) {
- if (!controller_thread_is_enabled(is_normal, global->thread)) break;
+ if (!controller_main_thread_is_enabled(is_normal, global->thread)) break;
// Re-establish global instance read lock to wait for or protect from the cleanup thread while checking the read instance.
status_lock = controller_lock_read(is_normal, global->thread, &global->thread->lock.instance);
return status_lock;
}
- if (!controller_thread_is_enabled(is_normal, global->thread)) return F_status_set_error(F_interrupt);
+ if (!controller_main_thread_is_enabled(is_normal, global->thread)) return F_status_set_error(F_interrupt);
if (F_status_set_fine(status) == F_require) return status;
if (required_not_run) return F_require;
while (controller_main_thread_is_enabled(is_normal, global->thread)) {
- controller_time_now(controller_thread_exit_ready_timeout_seconds_d, controller_thread_exit_ready_timeout_nanoseconds_d, &time);
+ controller_time_now(controller_main_thread_exit_ready_timeout_seconds_d, controller_main_thread_exit_ready_timeout_nanoseconds_d, &time);
memset((void *) &information, 0, sizeof(siginfo_t));
if (information.si_signo == F_signal_interrupt || information.si_signo == F_signal_abort || information.si_signo == F_signal_quit || information.si_signo == F_signal_termination) {
global->thread->signal = information.si_signo;
- controller_thread_instance_cancel(global, is_normal, controller_thread_cancel_signal_e);
+ controller_main_thread_instance_cancel(global, is_normal, controller_thread_cancel_signal_e);
break;
}
#endif // _di_controller_main_thread_signal_
#ifndef _di_controller_main_thread_signal_state_fss_
- f_status_t controller_main_thread_signal_state_fss(f_state_t * const state, void * const internal) {
+ void controller_main_thread_signal_state_fss(f_state_t * const state, void * const internal) {
- if (!state || !state->custom || !internal) return F_interrupt_not;
+ if (!state || !state->custom) return;
- controller_interrupt_t * const custom = (controller_interrupt_t *) state->custom;
- controller_thread_t * const thread = custom->thread;
+ controller_global_t * const global = (controller_global_t *) state->custom;
- if (!controller_main_thread_is_enabled(custom->is_normal, thread)) {
- return F_status_set_error(F_interrupt);
+ if (!controller_thread_is_enabled(custom->is_normal, global->thread)) {
+ global->main->program.signal_received = F_signal_abort;
+ global->main->setting.state.status = F_status_set_error(F_interrupt);
}
-
- if (thread->signal == F_signal_interrupt || thread->signal == F_signal_abort || thread->signal == F_signal_quit || thread->signal == F_signal_termination) {
- return F_status_set_error(F_interrupt);
+ else if (global->thread->signal == F_signal_interrupt || global->thread->signal == F_signal_abort || global->thread->signal == F_signal_quit || global->thread->signal == F_signal_termination) {
+ global->main->program.signal_received = F_signal_abort;
+ global->main->setting.state.status = F_status_set_error(F_interrupt);
}
-
- return F_interrupt_not;
}
#endif // _di_controller_main_thread_signal_state_fss_
#ifndef _di_controller_main_thread_signal_state_iki_
- f_status_t controller_main_thread_signal_state_iki(f_state_t * const state, void * const internal) {
+ void controller_main_thread_signal_state_iki(f_state_t * const state, void * const internal) {
- if (!state || !state->custom || !internal) return F_interrupt_not;
+ if (!state || !state->custom) return;
- controller_interrupt_t * const custom = (controller_interrupt_t *) state->custom;
- controller_thread_t * const thread = custom->thread;
+ controller_global_t * const global = (controller_global_t *) state->custom;
- if (!controller_main_thread_is_enabled(custom->is_normal, thread)) {
- return F_status_set_error(F_interrupt);
+ if (!controller_thread_is_enabled(custom->is_normal, global->thread)) {
+ global->main->program.signal_received = F_signal_abort;
+ global->main->setting.state.status = F_status_set_error(F_interrupt);
}
-
- if (thread->signal == F_signal_interrupt || thread->signal == F_signal_abort || thread->signal == F_signal_quit || thread->signal == F_signal_termination) {
- return F_status_set_error(F_interrupt);
+ else if (global->thread->signal == F_signal_interrupt || global->thread->signal == F_signal_abort || global->thread->signal == F_signal_quit || global->thread->signal == F_signal_termination) {
+ global->main->program.signal_received = F_signal_abort;
+ global->main->setting.state.status = F_status_set_error(F_interrupt);
}
-
- return F_interrupt_not;
}
#endif // _di_controller_main_thread_signal_state_iki_
f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
- controller_main_thread_signal((controller_global_t *) global, F_true);
+ controller_main_thread_signal((controller_global_t * const) global, F_true);
return 0;
}
f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
- controller_main_thread_signal((controller_global_t *) global, F_false);
+ controller_main_thread_signal((controller_global_t * const) global, F_false);
return 0;
}
*
* @param state
* The state data.
+ *
+ * Must not be NULL.
* @param internal
* Not used.
- *
- * @return
- * F_interrupt_not if not interrupted.
- *
- * F_interrupt (with error bit) if interrupted.
*/
#ifndef _di_controller_main_thread_signal_state_fss_
- extern f_status_t controller_main_thread_signal_state_fss(f_state_t * const state, void * const internal);
+ extern void controller_main_thread_signal_state_fss(f_state_t * const state, void * const internal);
#endif // _di_controller_main_thread_signal_state_fss_
/**
*
* @param state
* The state data.
+ *
+ * Must not be NULL.
* @param internal
* Not used.
- *
- * @return
- * F_interrupt_not if not interrupted.
- *
- * F_interrupt (with error bit) if interrupted.
*/
#ifndef _di_controller_main_thread_signal_state_iki_
- extern f_status_t controller_main_thread_signal_state_iki(f_state_t * const state, void * const internal);
+ extern void controller_main_thread_signal_state_iki(f_state_t * const state, void * const internal);
#endif // _di_controller_main_thread_signal_state_iki_
/**
* The global structure.
* Must be of type controller_global_t.
*
+ * Must not be NULL.
+ *
* @return
* 0, always.
*
* The global structure.
* Must be of type controller_global_t.
*
+ * Must not be NULL.
+ *
* @return
* 0, always.
*
--- /dev/null
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_main_thread_control_
+ void * controller_main_thread_control(void * const arguments) {
+
+ if (!arguments) return 0;
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_global_t * const global = (controller_global_t * const) arguments;
+
+ if (global->thread->enabled != controller_thread_enabled_e) return 0;
+
+ f_status_t status = F_okay;
+
+ if (status == F_child) {
+
+ // A forked child process should de-allocate 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.
+ controller_thread_delete_simple(global->thread);
+ controller_process_delete(global->setting);
+ controller_main_delete(global->main);
+ }
+
+ return 0;
+ }
+#endif // _di_controller_main_thread_control_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the thread "control" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_thread_control_h
+#define _controller_main_thread_control_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling control requests and responses.
+ *
+ * @param arguments
+ * The global data.
+ * Must be of type controller_global_t.
+ * Must not be NULL.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_main_thread_control_
+ extern void * controller_main_thread_control(void * const arguments);
+#endif // _di_controller_main_thread_control_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_thread_control_h
--- /dev/null
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_main_thread_entry_
+ void * controller_main_thread_entry(void * const arguments) {
+
+ if (!arguments) return 0;
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_instance_t * const instance = (controller_instance_t * const ) arguments;
+
+ if (!controller_main_thread_is_enabled(F_true, instance->thread)) return 0;
+
+ controller_main_t * const main = instance->main;
+ controller_cache_t * const cache = &instance->thread->cache;
+ f_status_t * const status = &instance->thread->status;
+
+ *status = controller_entry_read(*instance->global, F_true, cache);
+
+ if (F_status_set_fine(*status) == F_interrupt) {
+ instance->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (F_status_is_error(*status)) {
+ instance->setting->ready = controller_setting_ready_fail_e;
+ }
+ else if (*status != F_child) {
+ *status = controller_entry_preprocess(*instance->global, F_true, cache);
+
+ if ((main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) && (main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e)) {
+ controller_entry_setting_validate(*instance->global, F_true, cache);
+ }
+ }
+
+ if (F_status_is_error_not(*status) && *status != F_child) {
+ if (!(main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) || (main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e)) {
+
+ if (instance->setting->instance.pid == controller_entry_pid_require_e && f_file_exists(instance->setting->path_pid, F_true) == F_true) {
+ if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
+ controller_lock_print(main->program.error.to, instance->thread);
+
+ fl_print_format("%r%[%QThe pid file '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
+ fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->path_pid, main->program.error.notable);
+ fl_print_format("%[' must not already exist.%]%r", main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
+
+ controller_unlock_print_flush(main->program.error.to, instance->thread);
+ }
+
+ instance->setting->ready = controller_setting_ready_fail_e;
+ *status = F_status_set_error(F_available_not);
+ }
+ else {
+ *status = controller_entry_process(instance->global, cache, F_false, F_true);
+
+ if (F_status_is_error(*status)) {
+ instance->setting->ready = controller_setting_ready_fail_e;
+
+ if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (instance->setting->flag & controller_setting_flag_failsafe_e)) {
+ const uint8_t original_enabled = instance->thread->enabled;
+
+ // Restore operating mode so that the failsafe can execute.
+ *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ instance->thread->enabled = controller_thread_enabled_e;
+
+ f_thread_mutex_unlock(&instance->thread->lock.alert);
+ }
+
+ // Restart the signal thread to allow for signals while operating the failsafe Items.
+ if (!instance->thread->id_signal) {
+ f_thread_create(0, &instance->thread->id_signal, &controller_main_thread_signal_normal, (void *) instance->global);
+ }
+
+ const f_status_t status_failsafe = controller_entry_process(instance->global, cache, F_true, F_true);
+
+ if (F_status_is_error(status_failsafe)) {
+ if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
+ controller_lock_print(main->program.error.to, instance->thread);
+
+ fl_print_format("%r%[%QFailed while processing requested failsafe item '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
+ fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->instance.items.array[instance->setting->failsafe_item_id].name, main->program.error.notable);
+ fl_print_format(f_string_format_sentence_end_quote_s.string, main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
+
+ controller_unlock_print_flush(main->program.error.to, instance->thread);
+ }
+
+ *status = F_status_set_error(F_failure);
+ }
+ else {
+
+ // Restore operating mode to value prior to failsafe mode.
+ *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ instance->thread->enabled = original_enabled;
+
+ f_thread_mutex_unlock(&instance->thread->lock.alert);
+ }
+
+ *status = F_failure;
+ }
+ }
+ }
+ else if (F_status_set_fine(*status) == F_interrupt) {
+ instance->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (*status != F_child) {
+ instance->setting->ready = controller_setting_ready_done_e;
+ }
+ }
+
+ if (F_status_is_error_not(*status) && *status != F_child && main->program.parameters.array[controller_parameter_validate_e].result == f_console_result_none_e && instance->setting->mode == controller_setting_mode_helper_e) {
+ f_time_spec_t time;
+ time.tv_sec = controller_main_thread_exit_helper_timeout_seconds_d;
+ time.tv_nsec = controller_main_thread_exit_helper_timeout_nanoseconds_d;
+
+ nanosleep(&time, 0);
+
+ controller_main_thread_process_cancel(*(instance->global), F_true, controller_thread_cancel_exit_e);
+ }
+ }
+ }
+
+ 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.
+ controller_thread_delete_simple(instance->thread);
+ controller_process_delete(instance->setting);
+ controller_main_delete(main);
+
+ // According to the manpages, pthread_exit() calls exit(0), which the value of main->program.child should be returned instead.
+ if (main->program.child) exit(main->program.child);
+
+ return 0;
+ }
+
+ f_thread_condition_signal_all(&instance->thread->lock.alert_condition);
+
+ return 0;
+ }
+#endif // _di_controller_main_thread_entry_
+
+#ifndef _di_controller_main_thread_exit_
+ void * controller_main_thread_exit(void * const arguments) {
+
+ if (!arguments) return 0;
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_main_entry_t *instance = (controller_main_entry_t *) arguments;
+
+ controller_main_t * const main = instance->main;
+ controller_cache_t * const cache = &instance->thread->cache;
+ f_status_t * const status = &instance->thread->status;
+
+ *status = controller_entry_read(*instance->global, F_false, cache);
+
+ if (F_status_set_fine(*status) == F_interrupt) {
+ instance->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (F_status_is_error(*status)) {
+ instance->setting->ready = controller_setting_ready_fail_e;
+ }
+ else if (*status == F_file_found_not) {
+ instance->setting->ready = controller_setting_ready_done_e;
+ }
+ else if (*status != F_child) {
+ *status = controller_entry_preprocess(*instance->global, F_false, cache);
+
+ if ((main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) && (main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e)) {
+ controller_entry_setting_validate(*instance->global, F_false, cache);
+ }
+ }
+
+ if (F_status_is_error_not(*status) && *status != F_child && *status != F_file_found_not) {
+ if (!(main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) || (main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e)) {
+
+ *status = controller_entry_process(instance->global, cache, F_false, F_false);
+
+ if (F_status_is_error(*status)) {
+ instance->setting->ready = controller_setting_ready_fail_e;
+
+ if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (instance->setting->flag & controller_setting_flag_failsafe_e)) {
+
+ const uint8_t original_enabled = instance->thread->enabled;
+
+ // Restore operating mode so that the failsafe can execute.
+ if (F_status_set_fine(*status) == F_execute) {
+ *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ instance->thread->enabled = controller_thread_enabled_exit_e;
+
+ f_thread_mutex_unlock(&instance->thread->lock.alert);
+ }
+
+ // Restart the signal thread to allow for signals while operating the failsafe Items.
+ if (!instance->thread->id_signal) {
+ f_thread_create(0, &instance->thread->id_signal, &controller_main_thread_signal_other, (void *) instance->global);
+ }
+ }
+
+ const f_status_t status_failsafe = controller_entry_process(instance->global, cache, F_true, F_false);
+
+ if (F_status_is_error(status_failsafe)) {
+ if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
+ controller_lock_print(main->program.error.to, instance->thread);
+
+ fl_print_format("%r%[%QFailed while processing requested failsafe item '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
+ fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->instance.items.array[instance->setting->failsafe_item_id].name, main->program.error.notable);
+ fl_print_format(f_string_format_sentence_end_quote_s.string, main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
+
+ controller_unlock_print_flush(main->program.error.to, instance->thread);
+ }
+
+ *status = F_status_set_error(F_failure);
+ }
+ else {
+
+ // Restore operating mode to value prior to failsafe mode.
+ *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ instance->thread->enabled = original_enabled;
+
+ f_thread_mutex_unlock(&instance->thread->lock.alert);
+ }
+
+ *status = F_failure;
+ }
+ }
+ }
+ else if (F_status_set_fine(*status) == F_interrupt) {
+ instance->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (*status != F_child) {
+ instance->setting->ready = controller_setting_ready_done_e;
+ }
+ }
+ }
+
+ 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.
+ controller_thread_delete_simple(instance->thread);
+ controller_process_delete(instance->setting);
+ controller_main_delete(main);
+
+ return 0;
+ }
+
+ if (F_status_is_error_not(f_thread_mutex_lock(&instance->thread->lock.alert))) {
+ instance->thread->enabled = controller_thread_enabled_not_e;
+
+ f_thread_mutex_unlock(&instance->thread->lock.alert);
+ }
+
+ f_thread_condition_signal_all(&instance->thread->lock.alert_condition);
+
+ return 0;
+ }
+#endif // _di_controller_main_thread_exit_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the thread "entry" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_thread_entry_h
+#define _controller_main_thread_entry_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling entry processing.
+ *
+ * This acts as the main rule thread during entry processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * @param arguments
+ * The global data.
+ * Must be of type controller_instance_t.
+ * Must not be NULL.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_main_thread_entry_
+ extern void * controller_main_thread_entry(void * const arguments);
+#endif // _di_controller_main_thread_entry_
+
+/**
+ * Thread for handling exit file processing.
+ *
+ * This acts as the main rule thread during exit processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * Do not confuse this with exiting a thread, this is the what process the exit files (similar to that of an entry file).
+ * Exit files process the "stop" action, whereas the Entry files process the "start" Action
+ *
+ * @param arguments
+ * The global data.
+ * Must be of type controller_instance_t.
+ * Must not be NULL.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_main_thread_exit_
+ extern void * controller_main_thread_exit(void * const arguments);
+#endif // _di_controller_main_thread_exit_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_thread_entry_h
extern "C" {
#endif
-#ifndef _di_controller_thread_instance_
- void controller_thread_instance(const uint8_t is_normal, controller_instance_t * const instance) {
+#ifndef _di_controller_main_thread_instance_
+ void controller_main_thread_instance(const uint8_t is_normal, controller_instance_t * const instance) {
if (!instance) return;
- if (!controller_main_thread_is_enabled(is_normal, (controller_thread_t *) instance->thread)) return;
+ if (!controller_main_thread_is_enabled(is_normal, (controller_thread_t * const) instance->thread)) return;
const f_status_t status = controller_rule_process_do(controller_process_option_asynchronous_d, instance);
// A forked child instance should deallocate memory on exit.
// It seems that this function doesn't return to the calling thread for a forked child instance, even with the "return 0;" below.
- // Deallocate as much as possible.
if (status == F_child) {
controller_thread_delete_simple(instance->main_thread);
controller_process_delete(instance->program);
if (instance->main_data->program.child) exit(instance->main_data->program.child);
}
}
-#endif // _di_controller_thread_instance_
+#endif // _di_controller_main_thread_instance_
-#ifndef _di_controller_thread_instance_cancel_
- void controller_thread_instance_cancel(controller_global_t * const global, const uint8_t is_normal, const uint8_t by) {
+#ifndef _di_controller_main_thread_instance_cancel_
+ void controller_main_thread_instance_cancel(controller_instance_t * const global, const uint8_t is_normal, const uint8_t by) {
if (!global) return;
f_signal_send(F_signal_kill, instance->childs.array[j]);
time.tv_sec = 0;
- time.tv_nsec = controller_thread_exit_process_cancel_wait_d;
+ time.tv_nsec = controller_main_thread_exit_process_cancel_wait_d;
instance->childs.array[j] = 0;
}
f_thread_mutex_unlock(&global->thread->lock.cancel);
}
-#endif // _di_controller_thread_instance_cancel_
+#endif // _di_controller_main_thread_instance_cancel_
-#ifndef _di_controller_thread_instance_exit_
- void controller_thread_instance_exit(controller_global_t * const global) {
+#ifndef _di_controller_main_thread_instance_exit_
+ void controller_main_thread_instance_exit(controller_global_t * const global) {
if (!global) return;
// Restart the signal thread to allow for signals while operating the Exit.
if (!global->thread->id_signal) {
- f_thread_create(0, &global->thread->id_signal, &controller_thread_signal_other, (void *) global);
+ f_thread_create(0, &global->thread->id_signal, &controller_main_thread_signal_other, (void *) global);
}
const controller_main_entry_t entry = macro_controller_main_entry_t_initialize_1(global, global->setting);
- f_status_t status = f_thread_create(0, &global->thread->id_entry, &controller_thread_exit, (void *) &entry);
+ f_status_t status = f_thread_create(0, &global->thread->id_entry, &controller_main_thread_exit, (void *) &entry);
if (F_status_is_error(status)) {
if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
break;
}
- controller_time(controller_thread_exit_ready_timeout_seconds_d, controller_thread_exit_ready_timeout_nanoseconds_d, &time);
+ controller_time(controller_main_thread_exit_ready_timeout_seconds_d, controller_main_thread_exit_ready_timeout_nanoseconds_d, &time);
status = f_thread_condition_wait_timed(&time, &global->thread->lock.alert_condition, &global->thread->lock.alert);
global->thread->id_signal = 0;
}
- controller_thread_instance_cancel(*global, F_false, controller_thread_cancel_exit_e);
+ controller_main_thread_instance_cancel(*global, F_false, controller_thread_cancel_exit_e);
}
else {
if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) {
}
}
}
-#endif // _di_controller_thread_instance_exit_
+#endif // _di_controller_main_thread_instance_exit_
-#ifndef _di_controller_thread_instance_normal_
- void * controller_thread_instance_normal(void * const arguments) {
+#ifndef _di_controller_main_thread_instance_normal_
+ void * controller_main_thread_instance_normal(void * const arguments) {
+
+ if (!arguments) return 0;
f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
- controller_thread_instance(F_true, (controller_instance_t *) arguments);
+ controller_main_thread_instance(F_true, (controller_instance_t * const) arguments);
return 0;
}
-#endif // _di_controller_thread_instance_normal_
+#endif // _di_controller_main_thread_instance_normal_
+
+#ifndef _di_controller_main_thread_instance_other_
+ void * controller_main_thread_instance_other(void * const arguments) {
-#ifndef _di_controller_thread_instance_other_
- void * controller_thread_instance_other(void * const arguments) {
+ if (!arguments) return 0;
f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
- controller_thread_instance(F_false, (controller_instance_t *) arguments);
+ controller_main_thread_instance(F_false, (controller_instance_t * const) arguments);
return 0;
}
-#endif // _di_controller_thread_instance_other_
+#endif // _di_controller_main_thread_instance_other_
#ifdef __cplusplus
} // extern "C"
* @param is_normal
* If F_true, then process as if this operates during a normal operation (entry and control).
* If F_false, then process as if this operates during a an exit operation.
- * @param process
- * The process data.
+ * @param instance
+ * The instance data.
+ * Must not be NULL.
*
* @see controller_rule_process_do()
*/
-#ifndef _di_controller_thread_instance_
- extern void controller_thread_instance(const uint8_t is_normal, controller_instance_t * const instance);
-#endif // _di_controller_thread_instance_
+#ifndef _di_controller_main_thread_instance_
+ extern void controller_main_thread_instance(const uint8_t is_normal, controller_instance_t * const instance);
+#endif // _di_controller_main_thread_instance_
/**
* Cancel all process threads.
*
* @param global
- * The global thread data.
+ * The global data.
+ * Must not be NULL.
*
* This does not alter global.main.setting.state.status.
* @param is_normal
* If controller_thread_cancel_call_e, then this was not called from within the signal handling thread, so cancel the signal thread.
* If controller_thread_cancel_execute_e, then this was called from within the Entry/Exit for executing a process, so cancel the signal thread but not the Entry thread.
*/
-#ifndef _di_controller_thread_instance_cancel_
- extern void controller_thread_instance_cancel(controller_global_t * const global, const uint8_t is_normal, const uint8_t by);
-#endif // _di_controller_thread_instance_cancel_
+#ifndef _di_controller_main_thread_instance_cancel_
+ extern void controller_main_thread_instance_cancel(controller_global_t * const global, const uint8_t is_normal, const uint8_t by);
+#endif // _di_controller_main_thread_instance_cancel_
/**
* Process the Exit file, if applicable.
*
* @param global
- * The global thread data.
+ * The global data.
+ * Must not be NULL.
*
* This does not alter global.main.setting.state.status.
*/
-#ifndef _di_controller_thread_instance_exit_
- extern void controller_thread_instance_exit(controller_global_t * const global);
-#endif // _di_controller_thread_instance_exit_
+#ifndef _di_controller_main_thread_instance_exit_
+ extern void controller_main_thread_instance_exit(controller_global_t * const global);
+#endif // _di_controller_main_thread_instance_exit_
/**
* Asynchronously execute a Rule process during normal operations.
*
* @param arguments
* The thread arguments.
- * Must be of type controller_data_t.
+ * Must be of type controller_global_t.
+ * Must not be NULL.
*
* @return
* 0, always.
*
- * @see controller_thread_instance()
+ * @see controller_main_thread_instance()
*/
-#ifndef _di_controller_thread_instance_normal_
- extern void * controller_thread_instance_normal(void * const arguments);
-#endif // _di_controller_thread_instance_normal_
+#ifndef _di_controller_main_thread_instance_normal_
+ extern void * controller_main_thread_instance_normal(void * const global);
+#endif // _di_controller_main_thread_instance_normal_
/**
* Asynchronously execute a Rule process during other operations.
*
* @param arguments
* The thread arguments.
- * Must be of type controller_data_t.
+ * Must not be NULL.
+ * Must be of type controller_instance_t.
*
* @return
* 0, always.
*
- * @see controller_thread_instance()
+ * @see controller_main_thread_instance()
*/
-#ifndef _di_controller_thread_instance_other_
- extern void * controller_thread_instance_other(void * const arguments);
-#endif // _di_controller_thread_instance_other_
+#ifndef _di_controller_main_thread_instance_other_
+ extern void * controller_main_thread_instance_other(void * const arguments);
+#endif // _di_controller_main_thread_instance_other_
#ifdef __cplusplus
} // extern "C"
--- /dev/null
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_main_thread_rule_
+ void * controller_main_thread_rule(void * const arguments) {
+
+ if (!arguments) return 0;
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_global_t * const global = (controller_global_t * const) arguments;
+
+ if (!controller_main_thread_is_enabled(F_true, global->thread)) return 0;
+
+ return 0;
+ }
+#endif // _di_controller_main_thread_rule_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the thread "rule" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_thread_rule_h
+#define _controller_main_thread_rule_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling rule processing.
+ *
+ * This acts as the main rule thread after entry processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * @todo the control thread should send commands to this thread, somehow.
+ *
+ * @param arguments
+ * The global data.
+ * Must be of type controller_global_t.
+ * Must not be NULL.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_main_thread_rule_
+ extern void * controller_main_thread_rule(void * const arguments);
+#endif // _di_controller_main_thread_rule_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_thread_rule_h