From: Kevin Day Date: Sun, 10 Aug 2025 03:00:52 +0000 (-0500) Subject: Update: Add locking to signal property handling. X-Git-Tag: 0.7.3~35 X-Git-Url: https://www.git.kevux.org/?a=commitdiff_plain;h=9177b31e38779a0706ff2a2126a7d09257f054fe;p=controller Update: Add locking to signal property handling. Add a read/write `signal` lock property to the locks. Use read and write locks on `signal` and `signal_received` as appropriate. --- diff --git a/sources/c/program/controller/init/signal.c b/sources/c/program/controller/init/signal.c index 2a64c7f..c01c219 100644 --- a/sources/c/program/controller/init/signal.c +++ b/sources/c/program/controller/init/signal.c @@ -36,9 +36,10 @@ 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) { - main->thread.signal = information.si_signo; + controller_thread_signal_set(&main->thread, information.si_signo); f_thread_condition_signal_all(&main->thread.lock.reap_condition); + controller_thread_instance_cancel(main, is_normal, controller_thread_cancel_signal_e); break; diff --git a/sources/c/program/controller/main/common/type/lock.h b/sources/c/program/controller/main/common/type/lock.h index 5734438..9d93c9a 100644 --- a/sources/c/program/controller/main/common/type/lock.h +++ b/sources/c/program/controller/main/common/type/lock.h @@ -25,6 +25,7 @@ extern "C" { * The print lock is intended to lock any activity printing to stdout/stderr. * The instance lock is intended to lock any activity on the instance structure. * The rule lock is intended to lock any activity on the rules structure. + * The signal lock is intended to lock any activity on the thread.signal and the program signal_received properties. * * Properties: * - flag: A set of flags associated with the locks. @@ -35,6 +36,7 @@ extern "C" { * - enable: The enable r/w lock. * - instance: The instance r/w lock. * - rule: The rule r/w lock. + * - signal: The signal r/w lock. * - alert_condition: The condition used to trigger alerts. * - reap_condition: The condition used to trigger zombie reaping. */ @@ -50,6 +52,7 @@ extern "C" { f_thread_lock_t enable; f_thread_lock_t instance; f_thread_lock_t rule; + f_thread_lock_t signal; f_thread_condition_t alert_condition; f_thread_condition_t reap_condition; @@ -64,6 +67,7 @@ extern "C" { f_thread_lock_t_initialize, \ f_thread_lock_t_initialize, \ f_thread_lock_t_initialize, \ + f_thread_lock_t_initialize, \ f_thread_condition_t_initialize, \ f_thread_condition_t_initialize, \ } diff --git a/sources/c/program/controller/main/process.c b/sources/c/program/controller/main/process.c index b87347e..f310135 100644 --- a/sources/c/program/controller/main/process.c +++ b/sources/c/program/controller/main/process.c @@ -25,6 +25,7 @@ extern "C" { fll_program_print_copyright(&main->program.message, fll_program_copyright_year_author_s); } + // Threads are not enabled at this point so it is safe to directly access signal_received. if (main->program.signal_received) { fll_program_print_signal_received(&main->program.warning, main->program.signal_received); } @@ -153,6 +154,7 @@ extern "C" { if (F_status_set_fine(status) == F_interrupt) { status = F_status_set_error(F_interrupt); + // No threads are enabled, so it should be safe to access thread.signal and alter signal_received. if (main->thread.signal) { main->program.signal_received = main->thread.signal; } @@ -161,6 +163,7 @@ extern "C" { status = F_status_is_error(status) ? F_status_set_error(F_failure) : F_okay; } + // No threads are enabled, so it should be safe to alter signal_received. if (main->program.signal_received) { fll_program_print_signal_received(&main->program.warning, main->program.signal_received); } diff --git a/sources/c/program/controller/main/thread/cleanup.c b/sources/c/program/controller/main/thread/cleanup.c index 7f032b3..f0d8400 100644 --- a/sources/c/program/controller/main/thread/cleanup.c +++ b/sources/c/program/controller/main/thread/cleanup.c @@ -51,7 +51,7 @@ extern "C" { } // Reaping child processes is not critical, so don't care if the reap flag cannot be unset. - if (!main->thread.signal && F_status_is_error_not(status)) { + if (!controller_thread_signal_get(&main->thread) && F_status_is_error_not(status)) { memset((void *) &signal_data, 0, sizeof(siginfo_t)); reap_count = 0; diff --git a/sources/c/program/controller/main/thread/instance.c b/sources/c/program/controller/main/thread/instance.c index 96d357c..e446e65 100644 --- a/sources/c/program/controller/main/thread/instance.c +++ b/sources/c/program/controller/main/thread/instance.c @@ -129,7 +129,7 @@ extern "C" { return; } - for (; i < main->thread.instances.used; ++i) { + for (int signal = 0; i < main->thread.instances.used; ++i) { if (!main->thread.instances.array[i]) continue; @@ -143,7 +143,9 @@ extern "C" { for (j = 0; j < instance->childs.used; ++j) { if (instance->childs.array[j] > 0) { - f_signal_send(main->thread.signal ? main->thread.signal : F_signal_termination, instance->childs.array[j]); + signal = controller_thread_signal_get(&main->thread); + + f_signal_send(signal ? signal : F_signal_termination, instance->childs.array[j]); } } // for @@ -153,7 +155,9 @@ extern "C" { status = controller_file_pid_read(instance->path_pids.array[j], &pid); if (pid) { - f_signal_send(main->thread.signal ? main->thread.signal : F_signal_termination, pid); + signal = controller_thread_signal_get(&main->thread); + + f_signal_send(signal ? signal : F_signal_termination, pid); } } } // for diff --git a/sources/c/program/controller/main/thread/signal.c b/sources/c/program/controller/main/thread/signal.c index 28865be..1008861 100644 --- a/sources/c/program/controller/main/thread/signal.c +++ b/sources/c/program/controller/main/thread/signal.c @@ -36,9 +36,10 @@ 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) { - main->thread.signal = information.si_signo; + controller_thread_signal_set(&main->thread, information.si_signo); f_thread_condition_signal_all(&main->thread.lock.reap_condition); + controller_thread_instance_cancel(main, is_normal, controller_thread_cancel_signal_e); break; @@ -47,6 +48,75 @@ extern "C" { } #endif // _di_controller_thread_signal_ +#ifndef _di_controller_thread_signal_get_ + int controller_thread_signal_get(controller_thread_t * const thread) { + + if (!thread) return 0; + + if (f_thread_lock_read_try(&thread->lock.signal) != F_okay) { + for (f_time_spec_t time; ; ) { + + memset(&time, 0, sizeof(f_time_spec_t)); + + controller_time_now(controller_thread_timeout_lock_read_seconds_d, controller_thread_timeout_lock_read_nanoseconds_d, &time); + if (f_thread_lock_read_timed(&time, &thread->lock.signal) == F_okay) break; + } // for + } + + const int signal = thread->signal; + + f_thread_unlock(&thread->lock.signal); + + return signal; + } +#endif // _di_controller_thread_signal_get_ + +#ifndef _di_controller_thread_signal_received_set_ + f_status_t controller_thread_signal_received_set(controller_t * const main, const uint32_t value) { + + if (!main) return F_status_set_error(F_parameter); + + if (f_thread_lock_write_try(&main->thread.lock.signal) != F_okay) { + for (f_time_spec_t time; ; ) { + + memset(&time, 0, sizeof(f_time_spec_t)); + + controller_time_now(controller_thread_timeout_lock_write_seconds_d, controller_thread_timeout_lock_write_nanoseconds_d, &time); + if (f_thread_lock_write_timed(&time, &main->thread.lock.signal) == F_okay) break; + } // for + } + + main->program.signal_received = value; + + f_thread_unlock(&main->thread.lock.signal); + + return F_okay; + } +#endif // _di_controller_thread_signal_received_set_ + +#ifndef _di_controller_thread_signal_set_ + f_status_t controller_thread_signal_set(controller_thread_t * const thread, const int value) { + + if (!thread) return F_status_set_error(F_parameter); + + if (f_thread_lock_write_try(&thread->lock.signal) != F_okay) { + for (f_time_spec_t time; ; ) { + + memset(&time, 0, sizeof(f_time_spec_t)); + + controller_time_now(controller_thread_timeout_lock_write_seconds_d, controller_thread_timeout_lock_write_nanoseconds_d, &time); + if (f_thread_lock_write_timed(&time, &thread->lock.signal) == F_okay) break; + } // for + } + + thread->signal = value; + + f_thread_unlock(&thread->lock.signal); + + return F_okay; + } +#endif // _di_controller_thread_signal_set_ + #ifndef _di_controller_thread_signal_state_fss_ void controller_thread_signal_state_fss(f_state_t * const state, void * const internal) { @@ -57,10 +127,14 @@ extern "C" { if (!interrupt->main) return; if (!controller_thread_enable_is(&interrupt->main->thread, interrupt->is_normal)) { - interrupt->main->program.signal_received = F_signal_abort; + controller_thread_signal_received_set(interrupt->main, F_signal_abort); } - else if (interrupt->main->thread.signal == F_signal_interrupt || interrupt->main->thread.signal == F_signal_abort || interrupt->main->thread.signal == F_signal_quit || interrupt->main->thread.signal == F_signal_termination) { - interrupt->main->program.signal_received = F_signal_abort; + else { + const int signal = controller_thread_signal_get(&interrupt->main->thread); + + if (signal == F_signal_interrupt || signal == F_signal_abort || signal == F_signal_quit || signal == F_signal_termination) { + controller_thread_signal_received_set(interrupt->main, F_signal_abort); + } } } #endif // _di_controller_thread_signal_state_fss_ @@ -75,10 +149,14 @@ extern "C" { if (!interrupt->main) return; if (!controller_thread_enable_is(&interrupt->main->thread, interrupt->is_normal)) { - interrupt->main->program.signal_received = F_signal_abort; + controller_thread_signal_received_set(interrupt->main, F_signal_abort); } - else if (interrupt->main->thread.signal == F_signal_interrupt || interrupt->main->thread.signal == F_signal_abort || interrupt->main->thread.signal == F_signal_quit || interrupt->main->thread.signal == F_signal_termination) { - interrupt->main->program.signal_received = F_signal_abort; + else { + const int signal = controller_thread_signal_get(&interrupt->main->thread); + + if (signal == F_signal_interrupt || signal == F_signal_abort || signal == F_signal_quit || signal == F_signal_termination) { + controller_thread_signal_received_set(interrupt->main, F_signal_abort); + } } } #endif // _di_controller_thread_signal_state_iki_ diff --git a/sources/c/program/controller/main/thread/signal.h b/sources/c/program/controller/main/thread/signal.h index ea2e925..222705e 100644 --- a/sources/c/program/controller/main/thread/signal.h +++ b/sources/c/program/controller/main/thread/signal.h @@ -28,6 +28,99 @@ #endif // _di_controller_thread_signal_ /** + * Get the signal state of the thread in a thread-safe manner. + * + * This infinitely tries to obtain the read lock. + * + * Locks used on run: + * - main.thread.lock.signal (read lock). + * + * Locks held on exit: + * - None. + * + * @param thread + * The thread data. + * + * Must not be NULL. + * + * @return + * The current signal code. + * + * @see controller_time_now() + * + * @see f_thread_lock_read_timed() + * @see f_thread_lock_read_try() + * @see f_thread_unlock() + */ +#ifndef _di_controller_thread_signal_get_ + extern int controller_thread_signal_get(controller_thread_t * const thread); +#endif // _di_controller_thread_signal_get_ + +/** + * Set the signal received of the main program in a thread-safe manner. + * + * This infinitely tries to obtain the write lock. + * + * Locks used on run: + * - main.thread.lock.signal (write lock). + * + * Locks held on exit: + * - None. + * + * @param main + * The main program data. + * + * Must not be NULL. + * + * This does not alter main.setting.state.status. + * @param value + * The new value to assign to the thread signal. + * + * @return + * F_okay on success. + * + * F_parameter (with error bit) if a parmeter is invalid. + * + * @see f_thread_lock_write_timed() + * @see f_thread_lock_write_try() + * @see f_thread_unlock() + */ +#ifndef _di_controller_thread_signal_received_set_ + extern f_status_t controller_thread_signal_received_set(controller_t * const main, const uint32_t value); +#endif // _di_controller_thread_signal_received_set_ + +/** + * Set the signal of the thread in a thread-safe manner. + * + * This infinitely tries to obtain the write lock. + * + * Locks used on run: + * - main.thread.lock.signal (write lock). + * + * Locks held on exit: + * - None. + * + * @param thread + * The thread data. + * + * Must not be NULL. + * @param value + * The new value to assign to the thread signal. + * + * @return + * F_okay on success. + * + * F_parameter (with error bit) if a parmeter is invalid. + * + * @see f_thread_lock_write_timed() + * @see f_thread_lock_write_try() + * @see f_thread_unlock() + */ +#ifndef _di_controller_thread_signal_set_ + extern f_status_t controller_thread_signal_set(controller_thread_t * const thread, const int value); +#endif // _di_controller_thread_signal_set_ + +/** * Callback passed to FSS functions for checking for interrupts. * * @param state