]> Kevux Git Server - controller/commitdiff
Update: Add locking to signal property handling.
authorKevin Day <Kevin@kevux.org>
Sun, 10 Aug 2025 03:00:52 +0000 (22:00 -0500)
committerKevin Day <Kevin@kevux.org>
Sun, 10 Aug 2025 03:00:52 +0000 (22:00 -0500)
Add a read/write `signal` lock property to the locks.

Use read and write locks on `signal` and `signal_received` as appropriate.

sources/c/program/controller/init/signal.c
sources/c/program/controller/main/common/type/lock.h
sources/c/program/controller/main/process.c
sources/c/program/controller/main/thread/cleanup.c
sources/c/program/controller/main/thread/instance.c
sources/c/program/controller/main/thread/signal.c
sources/c/program/controller/main/thread/signal.h

index 2a64c7f3452aa27675d91d702cd318c689c1b1fb..c01c21979587a570839d734d50da37ccf18240e7 100644 (file)
@@ -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;
index 5734438804d8d008aec899bc23aa1e72b2c827b7..9d93c9ab73cb5cd9dff5d0ca6d4accccb82558ec 100644 (file)
@@ -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, \
   }
index b87347eaa3ffeb6feebcfc8359743d1afad9c616..f3101358e93922d86d5ebdeb2d8d37e1e644153b 100644 (file)
@@ -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);
     }
index 7f032b38982c06a21b0adac04c10e653914d3f1d..f0d8400fa8c678db8517279c921e4ca8f40618a9 100644 (file)
@@ -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;
index 96d357c7ccc7449aa9cf1609e8bfc1af19ab017f..e446e6510fe0ad7a5204a004c47ebb396d477680 100644 (file)
@@ -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
index 28865be07e0bba9b3e69f5fa962b6ed2b0013a7a..10088617883f8954db5179121d3d9b3d8739cc97 100644 (file)
@@ -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_
index ea2e92599f8e5f7b7f88798e8b22471272a8aa97..222705e28bc02637a1ba8c123a8a6b155f257c36 100644 (file)
 #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