]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] signals: support redistribution of signal zero when stopping
authorWilly Tarreau <w@1wt.eu>
Fri, 27 Aug 2010 16:26:11 +0000 (18:26 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 27 Aug 2010 16:26:11 +0000 (18:26 +0200)
Signal zero is never delivered by the system. However having a signal to
which functions and tasks can subscribe to be notified of a stopping event
is useful. So this patch does two things :
  1) allow signal zero to be delivered from any function of signal handler
  2) make soft_stop() deliver this signal so that tasks can be notified of
     a stopping condition.

include/proto/signal.h
src/haproxy.c
src/proxy.c
src/signal.c

index 5b5a64aea32bae171464e1cc16eb5c520c860f5d..ef43ef9baa2132e66939b29ff0ce0f9dbd90af4c 100644 (file)
@@ -19,6 +19,7 @@ extern int signal_queue_len;
 extern struct signal_descriptor signal_state[];
 extern struct pool_head *pool2_sig_handlers;
 
+void signal_handler(int sig);
 void __signal_process_queue();
 int signal_init();
 void deinit_signals();
index d9c3b6a4e93658883db850f32b97d864ccb01177..0bbed6f662404f737d2db09220ff391a1292f9bb 100644 (file)
@@ -244,7 +244,9 @@ void usage(char *name)
 /*********************************************************************/
 
 /*
- * upon SIGUSR1, let's have a soft stop.
+ * upon SIGUSR1, let's have a soft stop. Note that soft_stop() broadcasts
+ * a signal zero to all subscribers. This means that it's as easy as
+ * subscribing to signal 0 to get informed about an imminent shutdown.
  */
 void sig_soft_stop(struct sig_handler *sh)
 {
index 2949ec7f9a3676fe19bd34b167745ced960a8b8c..346d0b3e306d7e91beb4be9fc985bfa19ca68e42 100644 (file)
@@ -35,6 +35,7 @@
 #include <proto/proto_tcp.h>
 #include <proto/proto_http.h>
 #include <proto/proxy.h>
+#include <proto/signal.h>
 
 
 int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */
@@ -568,6 +569,8 @@ void soft_stop(void)
                }
                p = p->next;
        }
+       /* signal zero is used to broadcast the "stopping" event */
+       signal_handler(0);
 }
 
 
index 746ae9e0c3fe63977e9db63a9cf92ecbb76df188..128a3838fb27443bf8a2beeadfe4bf07457bf3c1 100644 (file)
@@ -29,9 +29,13 @@ int signal_queue[MAX_SIGNAL];                     /* in-order queue of received
 struct signal_descriptor signal_state[MAX_SIGNAL];
 struct pool_head *pool2_sig_handlers = NULL;
 sigset_t blocked_sig;
+int signal_pending = 0; /* non-zero if t least one signal remains unprocessed */
 
-/* Common signal handler, used by all signals. Received signals are queued. */
-static void signal_handler(int sig)
+/* 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.
+ */
+void signal_handler(int sig)
 {
        if (sig < 0 || sig > MAX_SIGNAL) {
                /* unhandled signal */
@@ -47,8 +51,10 @@ static void signal_handler(int sig)
                else
                        qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig);
        }
+
        signal_state[sig].count++;
-       signal(sig, signal_handler); /* re-arm signal */
+       if (sig)
+               signal(sig, signal_handler); /* re-arm signal */
 }
 
 /* Call handlers of all pending signals and clear counts and queue length. The
@@ -66,6 +72,11 @@ void __signal_process_queue()
        /* block signal delivery during processing */
        sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig);
 
+       /* It is important that we scan the queue forwards so that we can
+        * catch any signal that would have been queued by another signal
+        * handler. That allows real signal handlers to redistribute signals
+        * to tasks subscribed to signal zero.
+        */
        for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) {
                sig  = signal_queue[cur_pos];
                desc = &signal_state[sig];
@@ -121,7 +132,9 @@ void deinit_signals()
  * newly allocated sig_handler is returned, or NULL in case of any error. The
  * caller is responsible for unregistering the function when not used anymore.
  * Note that passing a NULL as the function pointer enables interception of the
- * signal without processing, which is identical to SIG_IGN.
+ * signal without processing, which is identical to SIG_IGN. If the signal is
+ * zero (which the system cannot deliver), only internal functions will be able
+ * to notify the registered functions.
  */
 struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg)
 {
@@ -130,7 +143,8 @@ struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler
        if (sig < 0 || sig > MAX_SIGNAL)
                return NULL;
 
-       signal(sig, signal_handler);
+       if (sig)
+               signal(sig, signal_handler);
 
        if (!fct)
                return NULL;
@@ -150,7 +164,9 @@ struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler
  * allocated sig_handler is returned, or NULL in case of any error. The caller
  * is responsible for unregistering the task when not used anymore. Note that
  * passing a NULL as the task pointer enables interception of the signal
- * without processing, which is identical to SIG_IGN.
+ * without processing, which is identical to SIG_IGN. If the signal is zero
+ * (which the system cannot deliver), only internal functions will be able to
+ * notify the registered functions.
  */
 struct sig_handler *signal_register_task(int sig, struct task *task, int reason)
 {
@@ -159,7 +175,8 @@ struct sig_handler *signal_register_task(int sig, struct task *task, int reason)
        if (sig < 0 || sig > MAX_SIGNAL)
                return NULL;
 
-       signal(sig, signal_handler);
+       if (sig)
+               signal(sig, signal_handler);
 
        if (!task)
                return NULL;