[MEDIUM] signals: support redistribution of signal zero when stopping

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.
This commit is contained in:
Willy Tarreau 2010-08-27 18:26:11 +02:00
parent 24f4efa670
commit d0807c3c60
4 changed files with 31 additions and 8 deletions

View File

@ -19,6 +19,7 @@ extern int signal_queue_len;
extern struct signal_descriptor signal_state[]; extern struct signal_descriptor signal_state[];
extern struct pool_head *pool2_sig_handlers; extern struct pool_head *pool2_sig_handlers;
void signal_handler(int sig);
void __signal_process_queue(); void __signal_process_queue();
int signal_init(); int signal_init();
void deinit_signals(); void deinit_signals();

View 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) void sig_soft_stop(struct sig_handler *sh)
{ {

View File

@ -35,6 +35,7 @@
#include <proto/proto_tcp.h> #include <proto/proto_tcp.h>
#include <proto/proto_http.h> #include <proto/proto_http.h>
#include <proto/proxy.h> #include <proto/proxy.h>
#include <proto/signal.h>
int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */ int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */
@ -568,6 +569,8 @@ void soft_stop(void)
} }
p = p->next; p = p->next;
} }
/* signal zero is used to broadcast the "stopping" event */
signal_handler(0);
} }

View File

@ -29,9 +29,13 @@ int signal_queue[MAX_SIGNAL]; /* in-order queue of received
struct signal_descriptor signal_state[MAX_SIGNAL]; struct signal_descriptor signal_state[MAX_SIGNAL];
struct pool_head *pool2_sig_handlers = NULL; struct pool_head *pool2_sig_handlers = NULL;
sigset_t blocked_sig; 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. */ /* Common signal handler, used by all signals. Received signals are queued.
static void signal_handler(int sig) * 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) { if (sig < 0 || sig > MAX_SIGNAL) {
/* unhandled signal */ /* unhandled signal */
@ -47,8 +51,10 @@ static void signal_handler(int sig)
else else
qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig); qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig);
} }
signal_state[sig].count++; 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 /* 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 */ /* block signal delivery during processing */
sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig); 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++) { for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) {
sig = signal_queue[cur_pos]; sig = signal_queue[cur_pos];
desc = &signal_state[sig]; 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 * 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. * caller is responsible for unregistering the function when not used anymore.
* Note that passing a NULL as the function pointer enables interception of the * 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) 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) if (sig < 0 || sig > MAX_SIGNAL)
return NULL; return NULL;
signal(sig, signal_handler); if (sig)
signal(sig, signal_handler);
if (!fct) if (!fct)
return NULL; 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 * 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 * is responsible for unregistering the task when not used anymore. Note that
* passing a NULL as the task pointer enables interception of the signal * 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) 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) if (sig < 0 || sig > MAX_SIGNAL)
return NULL; return NULL;
signal(sig, signal_handler); if (sig)
signal(sig, signal_handler);
if (!task) if (!task)
return NULL; return NULL;