diff options
author | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
commit | 16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch) | |
tree | e6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_signal.c | |
download | unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2 |
Initial version.
Diffstat (limited to 'src/nxt_signal.c')
-rw-r--r-- | src/nxt_signal.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/nxt_signal.c b/src/nxt_signal.c new file mode 100644 index 00000000..e267e3eb --- /dev/null +++ b/src/nxt_signal.c @@ -0,0 +1,230 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +/* + * Signals are handled only via a main thread event engine work queue. + * There are three ways to route signals to the work queue: + * + * 1) Using signal event notifications if an event facility supports it: + * kqueue and epoll/signalfd. This method is used regardless of thread mode. + * + * 2) Multi-threaded mode: a dedicated signal thread which waits in sigwait() + * and post a signal number to the main thread event engine. + * + * 3) Single-threaded mode: a signal handler which posts a signal number + * to the event engine. + */ + + +static nxt_int_t nxt_signal_action(int signo, void (*handler)(int)); + + +nxt_event_signals_t * +nxt_event_engine_signals(const nxt_event_sig_t *sigev) +{ + nxt_event_signals_t *signals; + + signals = nxt_zalloc(sizeof(nxt_event_signals_t)); + if (signals == NULL) { + return NULL; + } + + signals->sigev = sigev; + + if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) { + goto fail; + } + + if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) { + goto fail; + } + + sigemptyset(&signals->sigmask); + + while (sigev->signo != 0) { + sigaddset(&signals->sigmask, sigev->signo); + sigev++; + } + + if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) { + nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); + goto fail; + } + + return signals; + +fail: + + nxt_free(signals); + + return NULL; +} + + +static nxt_int_t +nxt_signal_action(int signo, void (*handler)(int)) +{ + struct sigaction sa; + + nxt_memzero(&sa, sizeof(struct sigaction)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + + if (sigaction(signo, &sa, NULL) == 0) { + return NXT_OK; + } + + nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno); + + return NXT_ERROR; +} + + +static void +nxt_signal_handler(int signo) +{ + nxt_thread_t *thr; + + thr = nxt_thread(); + + /* Thread is running in a single context now. */ + thr->time.signal++; + + nxt_thread_time_update(thr); + + nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo); + + nxt_event_engine_signal(thr->engine, signo); + + thr->time.signal--; +} + + +#if (NXT_THREADS) + +static void nxt_signal_thread(void *data); + + +nxt_int_t +nxt_signal_thread_start(nxt_event_engine_t *engine) +{ + nxt_thread_link_t *link; + const nxt_event_sig_t *sigev; + + if (engine->signals->process == nxt_pid) { + return NXT_OK; + } + + if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) { + nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); + return NXT_ERROR; + } + + /* + * kqueue sets signal handlers to SIG_IGN and sigwait() ignores + * them after the switch of event facility from "kqueue" to "select". + */ + + for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) { + if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) { + return NXT_ERROR; + } + } + + link = nxt_zalloc(sizeof(nxt_thread_link_t)); + + if (nxt_fast_path(link != NULL)) { + link->start = nxt_signal_thread; + link->data = engine; + + if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) { + engine->signals->process = nxt_pid; + return NXT_OK; + } + } + + return NXT_ERROR; +} + + +static void +nxt_signal_thread(void *data) +{ + int signo; + nxt_err_t err; + nxt_thread_t *thr; + nxt_event_engine_t *engine; + + engine = data; + + thr = nxt_thread(); + + nxt_main_log_debug("signal thread"); + + for ( ;; ) { + err = sigwait(&engine->signals->sigmask, &signo); + + nxt_thread_time_update(thr); + + if (nxt_fast_path(err == 0)) { + nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo); + + nxt_event_engine_signal(engine, signo); + + } else { + nxt_main_log_alert("sigwait() failed %E", err); + } + } +} + + +void +nxt_signal_thread_stop(nxt_event_engine_t *engine) +{ + nxt_thread_handle_t thread; + + thread = engine->signals->thread; + + nxt_thread_cancel(thread); + nxt_thread_wait(thread); +} + + +#else /* !(NXT_THREADS) */ + + +nxt_int_t +nxt_signal_handlers_start(nxt_event_engine_t *engine) +{ + const nxt_event_sig_t *sigev; + + for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) { + if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) { + return NXT_ERROR; + } + } + + if (sigprocmask(SIG_UNBLOCK, &engine->signals->sigmask, NULL) != 0) { + nxt_main_log_alert("sigprocmask(SIG_UNBLOCK) failed %E", nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} + + +void +nxt_signal_handlers_stop(nxt_event_engine_t *engine) +{ + if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) { + nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); + } +} + +#endif |