From fcf47068f9563d9d83f7ffbae518eb6e4eae4c0e Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 9 Jul 2024 21:30:32 -0500 Subject: [PATCH] Update: Provide fallback should the locks on exit fail to catch and update function using pointer. The previous commit 49a3d41d1c17734b0a8299b356f1e299386f4a42 removed the forced fallback to avoid potential race conditions. The reason for having that fallback is to ensure that the exit process is guaranteed and not potentially blocked by a lock. Provide an alternative that makes several attempts to properly lock on exit. Only when those extra attempts fails should the failsafe/fallback be used. Update controller_thread_process_cancel() to have the global data as a pointer. --- level_3/controller/c/common/private-thread.h | 4 + level_3/controller/c/entry/private-entry.c | 2 +- level_3/controller/c/thread/private-thread.c | 2 +- level_3/controller/c/thread/private-thread_entry.c | 8 +- .../controller/c/thread/private-thread_process.c | 155 ++++++++++++--------- .../controller/c/thread/private-thread_process.h | 20 ++- .../controller/c/thread/private-thread_signal.c | 2 +- 7 files changed, 117 insertions(+), 76 deletions(-) diff --git a/level_3/controller/c/common/private-thread.h b/level_3/controller/c/common/private-thread.h index 6b48d1c..ea3a232 100644 --- a/level_3/controller/c/common/private-thread.h +++ b/level_3/controller/c/common/private-thread.h @@ -66,6 +66,10 @@ extern "C" { #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_thread_exit_disable_force_times 12 + #define controller_thread_exit_disable_force_timeout_nanoseconds_d 10000000 // 0.01 seconds in nanoseconds. + #define controller_thread_exit_disable_force_timeout_seconds_d 0 + /** * States for enabled, designating how to stop the process. * diff --git a/level_3/controller/c/entry/private-entry.c b/level_3/controller/c/entry/private-entry.c index 7188b69..8742d43 100644 --- a/level_3/controller/c/entry/private-entry.c +++ b/level_3/controller/c/entry/private-entry.c @@ -1330,7 +1330,7 @@ extern "C" { return F_execute; } - controller_thread_process_cancel(*global, is_entry, is_entry ? controller_thread_cancel_execute_e : controller_thread_cancel_exit_execute_e, 0); + controller_thread_process_cancel(global, is_entry, is_entry ? controller_thread_cancel_execute_e : controller_thread_cancel_exit_execute_e, 0); int result = 0; int option = FL_execute_parameter_option_path_d; diff --git a/level_3/controller/c/thread/private-thread.c b/level_3/controller/c/thread/private-thread.c index e8cb336..3aa9c74 100644 --- a/level_3/controller/c/thread/private-thread.c +++ b/level_3/controller/c/thread/private-thread.c @@ -311,7 +311,7 @@ extern "C" { } } - controller_thread_process_cancel(global, F_true, controller_thread_cancel_call_e, 0); + controller_thread_process_cancel(&global, F_true, controller_thread_cancel_call_e, 0); controller_thread_process_exit(&global); diff --git a/level_3/controller/c/thread/private-thread_entry.c b/level_3/controller/c/thread/private-thread_entry.c index 8284345..e204f3f 100644 --- a/level_3/controller/c/thread/private-thread_entry.c +++ b/level_3/controller/c/thread/private-thread_entry.c @@ -125,7 +125,7 @@ extern "C" { nanosleep(&time, 0); - controller_thread_process_cancel(*(entry->global), F_true, controller_thread_cancel_exit_e, 0); + controller_thread_process_cancel(entry->global, F_true, controller_thread_cancel_exit_e, 0); } } } @@ -260,11 +260,7 @@ extern "C" { return 0; } - if (F_status_is_error_not(f_thread_mutex_lock(&entry->global->thread->lock.alert))) { - entry->global->thread->enabled = controller_thread_enabled_not_e; - - f_thread_mutex_unlock(&entry->global->thread->lock.alert); - } + controller_thread_process_exit_force_set_disable(entry->global); f_thread_condition_signal_all(&entry->global->thread->lock.alert_condition); diff --git a/level_3/controller/c/thread/private-thread_process.c b/level_3/controller/c/thread/private-thread_process.c index e11dae0..5e5c87b 100644 --- a/level_3/controller/c/thread/private-thread_process.c +++ b/level_3/controller/c/thread/private-thread_process.c @@ -43,13 +43,15 @@ extern "C" { #endif // _di_controller_thread_process_ #ifndef _di_controller_thread_process_cancel_ - void controller_thread_process_cancel(const controller_global_t global, const bool is_normal, const uint8_t by, controller_process_t * const caller) { + void controller_thread_process_cancel(const controller_global_t * const global, const bool is_normal, const uint8_t by, controller_process_t * const caller) { - f_thread_mutex_lock(&global.thread->lock.cancel); + if (!global) return; + + f_thread_mutex_lock(&global->thread->lock.cancel); // Only cancel when enabled. - if (!controller_thread_is_enabled(is_normal, global.thread)) { - f_thread_mutex_unlock(&global.thread->lock.cancel); + if (!controller_thread_is_enabled(is_normal, global->thread)) { + f_thread_mutex_unlock(&global->thread->lock.cancel); return; } @@ -65,10 +67,10 @@ extern "C" { pid_t pid = 0; if (is_normal) { - entry = &global.setting->entry; + entry = &global->setting->entry; } else { - entry = &global.setting->exit; + entry = &global->setting->exit; } // A simple but inaccurate interval counter (expect this to be replaced in the future). @@ -78,16 +80,16 @@ extern "C" { time.tv_sec = 0; time.tv_nsec = interval_nanoseconds; - if (global.setting->mode == controller_setting_mode_helper_e && global.main->parameters.array[controller_parameter_validate_e].result == f_console_result_none_e) { + if (global->setting->mode == controller_setting_mode_helper_e && global->main->parameters.array[controller_parameter_validate_e].result == f_console_result_none_e) { int value = 0; f_number_unsigned_t lapsed = 0; - for (i = 0; i < global.thread->processs.used; ++i) { + for (i = 0; i < global->thread->processs.used; ++i) { - if (!global.thread->processs.array[i]) continue; + if (!global->thread->processs.array[i]) continue; if (caller && i == caller->id) continue; - process = global.thread->processs.array[i]; + process = global->thread->processs.array[i]; if (!process->id_thread) continue; @@ -98,72 +100,72 @@ extern "C" { } // Use the alert lock to toggle enabled (using it as if it is a write like and a signal lock). - status = f_thread_mutex_lock(&global.thread->lock.alert); + status = f_thread_mutex_lock(&global->thread->lock.alert); if (F_status_is_error(status)) { - global.thread->enabled = controller_thread_enabled_not_e; + global->thread->enabled = controller_thread_enabled_not_e; } else { if (by == controller_thread_cancel_execute_e) { - global.thread->enabled = controller_thread_enabled_execute_e; + global->thread->enabled = controller_thread_enabled_execute_e; } else if (by == controller_thread_cancel_exit_e) { - global.thread->enabled = controller_thread_enabled_not_e; + global->thread->enabled = controller_thread_enabled_not_e; } else if (by == controller_thread_cancel_exit_execute_e) { - global.thread->enabled = controller_thread_enabled_exit_execute_e; + global->thread->enabled = controller_thread_enabled_exit_execute_e; } else { - global.thread->enabled = controller_thread_enabled_exit_e; + global->thread->enabled = controller_thread_enabled_exit_e; } - f_thread_mutex_unlock(&global.thread->lock.alert); + f_thread_mutex_unlock(&global->thread->lock.alert); } - if (global.thread->id_cleanup) { - f_thread_cancel(global.thread->id_cleanup); - f_thread_join(global.thread->id_cleanup, 0); + if (global->thread->id_cleanup) { + f_thread_cancel(global->thread->id_cleanup); + f_thread_join(global->thread->id_cleanup, 0); - global.thread->id_cleanup = 0; + global->thread->id_cleanup = 0; } - if (global.thread->id_control) { - f_thread_cancel(global.thread->id_control); - f_thread_join(global.thread->id_control, 0); + if (global->thread->id_control) { + f_thread_cancel(global->thread->id_control); + f_thread_join(global->thread->id_control, 0); - global.thread->id_control = 0; + global->thread->id_control = 0; } // The sigtimedwait() function that is run inside of signal must be interrupted via the f_thread_cancel(). - if (by != controller_thread_cancel_signal_e && global.thread->id_signal) { - f_thread_cancel(global.thread->id_signal); - f_thread_join(global.thread->id_signal, 0); + if (by != controller_thread_cancel_signal_e && global->thread->id_signal) { + f_thread_cancel(global->thread->id_signal); + f_thread_join(global->thread->id_signal, 0); - global.thread->id_signal = 0; + global->thread->id_signal = 0; } - if (global.setting->mode == controller_setting_mode_helper_e && global.main->parameters.array[controller_parameter_validate_e].result == f_console_result_none_e) { - f_thread_mutex_unlock(&global.thread->lock.cancel); + if (global->setting->mode == controller_setting_mode_helper_e && global->main->parameters.array[controller_parameter_validate_e].result == f_console_result_none_e) { + f_thread_mutex_unlock(&global->thread->lock.cancel); return; } - for (; i < global.thread->processs.used; ++i) { + for (; i < global->thread->processs.used; ++i) { - if (!global.thread->processs.array[i]) continue; + if (!global->thread->processs.array[i]) continue; if (caller && i == caller->id) continue; - process = global.thread->processs.array[i]; + process = global->thread->processs.array[i]; // Do not cancel exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) { + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) { continue; } for (j = 0; j < process->childs.used; ++j) { if (process->childs.array[j] > 0) { - f_signal_send(global.thread->signal ? global.thread->signal : F_signal_termination, process->childs.array[j]); + f_signal_send(global->thread->signal ? global->thread->signal : F_signal_termination, process->childs.array[j]); } } // for @@ -173,7 +175,7 @@ extern "C" { status = controller_file_pid_read(process->path_pids.array[j], &pid); if (pid) { - f_signal_send(global.thread->signal ? global.thread->signal : F_signal_termination, pid); + f_signal_send(global->thread->signal ? global->thread->signal : F_signal_termination, pid); } } } // for @@ -182,15 +184,15 @@ extern "C" { if (entry->timeout_exit && !(entry->flag & controller_entry_flag_timeout_exit_no_e)) { f_number_unsigned_t lapsed = 0; - for (i = 0; i < global.thread->processs.used && lapsed < entry->timeout_exit; ++i) { + for (i = 0; i < global->thread->processs.used && lapsed < entry->timeout_exit; ++i) { - if (!global.thread->processs.array[i]) continue; + if (!global->thread->processs.array[i]) continue; if (caller && i == caller->id) continue; - process = global.thread->processs.array[i]; + process = global->thread->processs.array[i]; // Do not wait for processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) { + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) { continue; } @@ -244,15 +246,15 @@ extern "C" { } // for } - for (i = 0; i < global.thread->processs.size; ++i) { + for (i = 0; i < global->thread->processs.size; ++i) { - if (!global.thread->processs.array[i]) continue; + if (!global->thread->processs.array[i]) continue; if (caller && i == caller->id) continue; - process = global.thread->processs.array[i]; + process = global->thread->processs.array[i]; // Do not kill exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) continue; + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) continue; if (process->id_thread) { if (process->childs.used) { @@ -280,7 +282,7 @@ extern "C" { for (j = 0; j < process->childs.size; ++j) { // Do not kill exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) continue; + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) continue; if (process->childs.array[j]) { @@ -298,7 +300,7 @@ extern "C" { for (j = 0; j < process->path_pids.used; ++j) { // Do not kill exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) continue; + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) continue; if (f_file_exists(process->path_pids.array[j], F_true) == F_true) { status = controller_file_pid_read(process->path_pids.array[j], &pid); @@ -317,7 +319,7 @@ extern "C" { while (process->childs.used) { // Do not shrink below an exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) break; + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) break; if (process->childs.array[j] > 0) break; --process->childs.used; @@ -327,14 +329,14 @@ extern "C" { while (process->path_pids.used) { // Do not shrink below an exit processes, when not performing "execute" during exit. - if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) break; + if (process->type == controller_process_type_exit_e && global->thread->enabled != controller_thread_enabled_exit_execute_e) break; if (process->path_pids.array[j].used) break; --process->path_pids.used; } // while } // for - f_thread_mutex_unlock(&global.thread->lock.cancel); + f_thread_mutex_unlock(&global->thread->lock.cancel); } #endif // _di_controller_thread_process_cancel_ @@ -369,11 +371,7 @@ extern "C" { controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_thread_create", F_true); } - if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) { - global->thread->enabled = controller_thread_enabled_not_e; - - f_thread_mutex_unlock(&global->thread->lock.alert); - } + controller_thread_process_exit_force_set_disable(global); } else { struct timespec time; @@ -390,13 +388,7 @@ extern "C" { } while (F_status_is_error_not(status) && global->thread->enabled == controller_thread_enabled_exit_e); - if (F_status_is_error(status)) { - if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) { - global->thread->enabled = controller_thread_enabled_not_e; - - f_thread_mutex_unlock(&global->thread->lock.alert); - } - } + if (F_status_is_error(status)) controller_thread_process_exit_force_set_disable(global); } // The sigtimedwait() function that is run inside of signal must be interrupted via the f_thread_cancel(). @@ -407,17 +399,48 @@ extern "C" { global->thread->id_signal = 0; } - controller_thread_process_cancel(*global, F_false, controller_thread_cancel_exit_e, 0); + controller_thread_process_cancel(global, F_false, controller_thread_cancel_exit_e, 0); } else { - if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) { + controller_thread_process_exit_force_set_disable(global); + } + } +#endif // _di_controller_thread_process_exit_ + +#ifndef _di_controller_thread_process_exit_force_set_disable_ + void controller_thread_process_exit_force_set_disable(const controller_global_t * const global) { + + if (!global) return; + + if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) { + global->thread->enabled = controller_thread_enabled_not_e; + + f_thread_mutex_unlock(&global->thread->lock.alert); + + return; + } + + struct timespec time; + + for (uint8_t i = 0; i < controller_thread_exit_disable_force_times; ++i) { + + memset((void *) &time, 0, sizeof(struct timespec)); + + controller_time(controller_thread_exit_disable_force_timeout_seconds_d, controller_thread_exit_disable_force_timeout_nanoseconds_d, &time); + + if (F_status_is_error_not(f_thread_mutex_lock_timed(&time, &global->thread->lock.alert))) { global->thread->enabled = controller_thread_enabled_not_e; f_thread_mutex_unlock(&global->thread->lock.alert); + + return; } - } + } // for + + // Forcibly set disable regardless of the risk. + global->thread->enabled = controller_thread_enabled_not_e; } -#endif // _di_controller_thread_process_exit_ +#endif // _di_controller_thread_process_exit_force_set_disable_ #ifndef _di_controller_thread_process_normal_ void * controller_thread_process_normal(void * const arguments) { diff --git a/level_3/controller/c/thread/private-thread_process.h b/level_3/controller/c/thread/private-thread_process.h index a636cba..97b4e29 100644 --- a/level_3/controller/c/thread/private-thread_process.h +++ b/level_3/controller/c/thread/private-thread_process.h @@ -46,7 +46,7 @@ extern "C" { * Set to NULL to not use. */ #ifndef _di_controller_thread_process_cancel_ - extern void controller_thread_process_cancel(const controller_global_t global, const bool is_normal, const uint8_t by, controller_process_t * const caller) F_attribute_visibility_internal_d; + extern void controller_thread_process_cancel(const controller_global_t * const global, const bool is_normal, const uint8_t by, controller_process_t * const caller) F_attribute_visibility_internal_d; #endif // _di_controller_thread_process_cancel_ /** @@ -60,6 +60,24 @@ extern "C" { #endif // _di_controller_thread_process_exit_ /** + * Set the execution state to disabled during exiting and force the case if need be. + * + * The program must exit during the Exit process. + * The state must be properly set. + * Perform a limited number of attempts to set the state to exiting. + * Should this fail, then force the case regardless of the risk. + * + * @param global + * The global thread data. + * + * The global.thread.lock.alert lock will be set and then unset if possible. + * The global.thread.enabled will be updated and set to controller_thread_enabled_not_e. + */ +#ifndef _di_controller_thread_process_exit_force_set_disable_ + extern void controller_thread_process_exit_force_set_disable(const controller_global_t * const global) F_attribute_visibility_internal_d; +#endif // _di_controller_thread_process_exit_force_set_disable_ + +/** * Asynchronously execute a Rule process during normal operations. * * @param arguments diff --git a/level_3/controller/c/thread/private-thread_signal.c b/level_3/controller/c/thread/private-thread_signal.c index 75cbc29..04f70b0 100644 --- a/level_3/controller/c/thread/private-thread_signal.c +++ b/level_3/controller/c/thread/private-thread_signal.c @@ -33,7 +33,7 @@ extern "C" { 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_process_cancel(*global, is_normal, controller_thread_cancel_signal_e, 0); + controller_thread_process_cancel(global, is_normal, controller_thread_cancel_signal_e, 0); break; } -- 1.8.3.1