MEDIUM: threads/signal: Add a lock to make signals thread-safe
A global lock has been added to protect the signal processing. So when a signal
it triggered, only one thread will catch it.
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index 19cdf83..53b4e3d 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -145,6 +145,7 @@
TASK_RQ_LOCK,
TASK_WQ_LOCK,
POOL_LOCK,
+ SIGNALS_LOCK,
LOCK_LABELS
};
struct lock_stat {
@@ -228,7 +229,8 @@
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++) {
diff --git a/include/proto/signal.h b/include/proto/signal.h
index 6556446..084fa7d 100644
--- a/include/proto/signal.h
+++ b/include/proto/signal.h
@@ -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 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();
diff --git a/src/haproxy.c b/src/haproxy.c
index ff63844..81122c8 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2819,6 +2819,10 @@
free(tids);
free(threads);
+#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
+ show_lock_stats();
+#endif
+
#endif /* USE_THREAD */
}
else {
diff --git a/src/signal.c b/src/signal.c
index 201c93f..14e4f1e 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -31,6 +31,10 @@
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 @@
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 @@
/* 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 @@
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 @@
pool_free2(pool2_sig_handlers, sh);
}
}
+ SPIN_DESTROY(&signals_lock);
}
/* Register a function and an integer argument on a signal. A pointer to the