]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: threads/signal: Add a lock to make signals thread-safe
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 30 May 2017 13:34:30 +0000 (15:34 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 31 Oct 2017 12:58:30 +0000 (13:58 +0100)
A global lock has been added to protect the signal processing. So when a signal
it triggered, only one thread will catch it.

include/common/hathreads.h
include/proto/signal.h
src/haproxy.c
src/signal.c

index 19cdf83dbf117a6da924ebb82b6e447901097768..53b4e3d7f9307ef96f96cfd934f6ef601ed25caa 100644 (file)
@@ -145,6 +145,7 @@ enum lock_label {
        TASK_RQ_LOCK,
        TASK_WQ_LOCK,
        POOL_LOCK,
+       SIGNALS_LOCK,
        LOCK_LABELS
 };
 struct lock_stat {
@@ -228,7 +229,8 @@ struct ha_rwlock {
 static inline void show_lock_stats()
 {
        const char *labels[LOCK_LABELS] = {"THREAD_SYNC", "FDTAB", "FDCACHE", "FD", "POLL",
-                                          "TASK_RQ", "TASK_WQ", "POOL" };
+                                          "TASK_RQ", "TASK_WQ", "POOL",
+                                          "SIGNALS" };
        int lbl;
 
        for (lbl = 0; lbl < LOCK_LABELS; lbl++) {
index 6556446ad3c8a3e2ec4999e15a7355bb484bd562..084fa7d151e0395d99b837fcf1bf299432d0e32b 100644 (file)
@@ -13,6 +13,8 @@
 
 #include <signal.h>
 #include <common/standard.h>
+#include <common/hathreads.h>
+
 #include <types/signal.h>
 #include <types/task.h>
 
@@ -20,6 +22,10 @@ extern int signal_queue_len;
 extern struct signal_descriptor signal_state[];
 extern struct pool_head *pool2_sig_handlers;
 
+#ifdef USE_THREAD
+extern HA_SPINLOCK_T signals_lock;
+#endif
+
 void signal_handler(int sig);
 void __signal_process_queue();
 int signal_init();
index ff638445eb6f1465626393ca88dfe6feea968b2a..81122c839e548516c01e04d8db11a8517a3ef526 100644 (file)
@@ -2819,6 +2819,10 @@ int main(int argc, char **argv)
                free(tids);
                free(threads);
 
+#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
+               show_lock_stats();
+#endif
+
 #endif /* USE_THREAD */
        }
        else {
index 201c93f4fa3ac782914baffa4e7ce729002a1096..14e4f1ef90956c567bc1aa9f11ede86feada4716 100644 (file)
@@ -31,6 +31,10 @@ struct pool_head *pool2_sig_handlers = NULL;
 sigset_t blocked_sig;
 int signal_pending = 0; /* non-zero if t least one signal remains unprocessed */
 
+#ifdef USE_THREAD
+HA_SPINLOCK_T signals_lock;
+#endif
+
 /* Common signal handler, used by all signals. Received signals are queued.
  * Signal number zero has a specific status, as it cannot be delivered by the
  * system, any function may call it to perform asynchronous signal delivery.
@@ -69,6 +73,9 @@ void __signal_process_queue()
        struct signal_descriptor *desc;
        sigset_t old_sig;
 
+       if (SPIN_TRYLOCK(SIGNALS_LOCK, &signals_lock))
+               return;
+
        /* block signal delivery during processing */
        sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig);
 
@@ -95,6 +102,7 @@ void __signal_process_queue()
 
        /* restore signal delivery */
        sigprocmask(SIG_SETMASK, &old_sig, NULL);
+       SPIN_UNLOCK(SIGNALS_LOCK, &signals_lock);
 }
 
 /* perform minimal intializations, report 0 in case of error, 1 if OK. */
@@ -106,6 +114,8 @@ int signal_init()
        memset(signal_queue, 0, sizeof(signal_queue));
        memset(signal_state, 0, sizeof(signal_state));
 
+       SPIN_INIT(&signals_lock);
+
        /* Ensure signals are not blocked. Some shells or service managers may
         * accidently block all of our signals unfortunately, causing lots of
         * zombie processes to remain in the background during reloads.
@@ -140,6 +150,7 @@ void deinit_signals()
                        pool_free2(pool2_sig_handlers, sh);
                }
        }
+       SPIN_DESTROY(&signals_lock);
 }
 
 /* Register a function and an integer argument on a signal. A pointer to the