[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 pool_head *pool2_sig_handlers;
void signal_handler(int sig);
void __signal_process_queue();
int signal_init();
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)
{

View 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);
}

View 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;