diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c index 68c32bb60d..4f47eaf2e3 100644 --- a/src/backend/libpq/pqsignal.c +++ b/src/backend/libpq/pqsignal.c @@ -95,3 +95,52 @@ pqinitmask(void) sigdelset(&StartupBlockSig, SIGALRM); #endif } + +/* + * Set up a postmaster signal handler for signal "signo" + * + * Returns the previous handler. + * + * This is used only in the postmaster, which has its own odd approach to + * signal handling. For signals with handlers, we block all signals for the + * duration of signal handler execution. We also do not set the SA_RESTART + * flag; this should be safe given the tiny range of code in which the + * postmaster ever unblocks signals. + * + * pqinitmask() must have been invoked previously. + * + * On Windows, this function is just an alias for pqsignal() + * (and note that it's calling the code in src/backend/port/win32/signal.c, + * not src/port/pqsignal.c). On that platform, the postmaster's signal + * handlers still have to block signals for themselves. + */ +pqsigfunc +pqsignal_pm(int signo, pqsigfunc func) +{ +#ifndef WIN32 + struct sigaction act, + oact; + + act.sa_handler = func; + if (func == SIG_IGN || func == SIG_DFL) + { + /* in these cases, act the same as pqsignal() */ + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + } + else + { + act.sa_mask = BlockSig; + act.sa_flags = 0; + } +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +#else /* WIN32 */ + return pqsignal(signo, func); +#endif +} diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 85f15a5666..5f30359165 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -606,14 +606,25 @@ PostmasterMain(int argc, char *argv[]) /* * Set up signal handlers for the postmaster process. * - * In the postmaster, we want to install non-ignored handlers *without* - * SA_RESTART. This is because they'll be blocked at all times except - * when ServerLoop is waiting for something to happen, and during that - * window, we want signals to exit the select(2) wait so that ServerLoop - * can respond if anything interesting happened. On some platforms, - * signals marked SA_RESTART would not cause the select() wait to end. - * Child processes will generally want SA_RESTART, but we expect them to - * set up their own handlers before unblocking signals. + * In the postmaster, we use pqsignal_pm() rather than pqsignal() (which + * is used by all child processes and client processes). That has a + * couple of special behaviors: + * + * 1. Except on Windows, we tell sigaction() to block all signals for the + * duration of the signal handler. This is faster than our old approach + * of blocking/unblocking explicitly in the signal handler, and it should + * also prevent excessive stack consumption if signals arrive quickly. + * + * 2. We do not set the SA_RESTART flag. This is because signals will be + * blocked at all times except when ServerLoop is waiting for something to + * happen, and during that window, we want signals to exit the select(2) + * wait so that ServerLoop can respond if anything interesting happened. + * On some platforms, signals marked SA_RESTART would not cause the + * select() wait to end. + * + * Child processes will generally want SA_RESTART, so pqsignal() sets that + * flag. We expect children to set up their own handlers before + * unblocking signals. * * CAUTION: when changing this list, check for side-effects on the signal * handling setup of child processes. See tcop/postgres.c, @@ -625,18 +636,16 @@ PostmasterMain(int argc, char *argv[]) pqinitmask(); PG_SETMASK(&BlockSig); - pqsignal_no_restart(SIGHUP, SIGHUP_handler); /* reread config file and - * have children do same */ - pqsignal_no_restart(SIGINT, pmdie); /* send SIGTERM and shut down */ - pqsignal_no_restart(SIGQUIT, pmdie); /* send SIGQUIT and die */ - pqsignal_no_restart(SIGTERM, pmdie); /* wait for children and shut down */ - pqsignal(SIGALRM, SIG_IGN); /* ignored */ - pqsignal(SIGPIPE, SIG_IGN); /* ignored */ - pqsignal_no_restart(SIGUSR1, sigusr1_handler); /* message from child - * process */ - pqsignal_no_restart(SIGUSR2, dummy_handler); /* unused, reserve for - * children */ - pqsignal_no_restart(SIGCHLD, reaper); /* handle child termination */ + pqsignal_pm(SIGHUP, SIGHUP_handler); /* reread config file and have + * children do same */ + pqsignal_pm(SIGINT, pmdie); /* send SIGTERM and shut down */ + pqsignal_pm(SIGQUIT, pmdie); /* send SIGQUIT and die */ + pqsignal_pm(SIGTERM, pmdie); /* wait for children and shut down */ + pqsignal_pm(SIGALRM, SIG_IGN); /* ignored */ + pqsignal_pm(SIGPIPE, SIG_IGN); /* ignored */ + pqsignal_pm(SIGUSR1, sigusr1_handler); /* message from child process */ + pqsignal_pm(SIGUSR2, dummy_handler); /* unused, reserve for children */ + pqsignal_pm(SIGCHLD, reaper); /* handle child termination */ /* * No other place in Postgres should touch SIGTTIN/SIGTTOU handling. We @@ -646,15 +655,15 @@ PostmasterMain(int argc, char *argv[]) * child processes should just allow the inherited settings to stand. */ #ifdef SIGTTIN - pqsignal(SIGTTIN, SIG_IGN); /* ignored */ + pqsignal_pm(SIGTTIN, SIG_IGN); /* ignored */ #endif #ifdef SIGTTOU - pqsignal(SIGTTOU, SIG_IGN); /* ignored */ + pqsignal_pm(SIGTTOU, SIG_IGN); /* ignored */ #endif /* ignore SIGXFSZ, so that ulimit violations work like disk full */ #ifdef SIGXFSZ - pqsignal(SIGXFSZ, SIG_IGN); /* ignored */ + pqsignal_pm(SIGXFSZ, SIG_IGN); /* ignored */ #endif /* @@ -2640,7 +2649,13 @@ SIGHUP_handler(SIGNAL_ARGS) { int save_errno = errno; + /* + * We rely on the signal mechanism to have blocked all signals ... except + * on Windows, which lacks sigaction(), so we have to do it manually. + */ +#ifdef WIN32 PG_SETMASK(&BlockSig); +#endif if (Shutdown <= SmartShutdown) { @@ -2700,7 +2715,9 @@ SIGHUP_handler(SIGNAL_ARGS) #endif } +#ifdef WIN32 PG_SETMASK(&UnBlockSig); +#endif errno = save_errno; } @@ -2714,7 +2731,13 @@ pmdie(SIGNAL_ARGS) { int save_errno = errno; + /* + * We rely on the signal mechanism to have blocked all signals ... except + * on Windows, which lacks sigaction(), so we have to do it manually. + */ +#ifdef WIN32 PG_SETMASK(&BlockSig); +#endif ereport(DEBUG2, (errmsg_internal("postmaster received signal %d", @@ -2880,7 +2903,9 @@ pmdie(SIGNAL_ARGS) break; } +#ifdef WIN32 PG_SETMASK(&UnBlockSig); +#endif errno = save_errno; } @@ -2895,7 +2920,13 @@ reaper(SIGNAL_ARGS) int pid; /* process id of dead child process */ int exitstatus; /* its exit status */ + /* + * We rely on the signal mechanism to have blocked all signals ... except + * on Windows, which lacks sigaction(), so we have to do it manually. + */ +#ifdef WIN32 PG_SETMASK(&BlockSig); +#endif ereport(DEBUG4, (errmsg_internal("reaping dead processes"))); @@ -3212,7 +3243,9 @@ reaper(SIGNAL_ARGS) PostmasterStateMachine(); /* Done with signal handler */ +#ifdef WIN32 PG_SETMASK(&UnBlockSig); +#endif errno = save_errno; } @@ -5114,7 +5147,13 @@ sigusr1_handler(SIGNAL_ARGS) { int save_errno = errno; + /* + * We rely on the signal mechanism to have blocked all signals ... except + * on Windows, which lacks sigaction(), so we have to do it manually. + */ +#ifdef WIN32 PG_SETMASK(&BlockSig); +#endif /* Process background worker state change. */ if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE)) @@ -5272,7 +5311,9 @@ sigusr1_handler(SIGNAL_ARGS) signal_child(StartupPID, SIGUSR2); } +#ifdef WIN32 PG_SETMASK(&UnBlockSig); +#endif errno = save_errno; } diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h index d0e3b9e479..46bc3a8895 100644 --- a/src/include/libpq/pqsignal.h +++ b/src/include/libpq/pqsignal.h @@ -36,4 +36,7 @@ extern sigset_t UnBlockSig, extern void pqinitmask(void); +/* pqsigfunc is declared in src/include/port.h */ +extern pqsigfunc pqsignal_pm(int signo, pqsigfunc func); + #endif /* PQSIGNAL_H */ diff --git a/src/include/port.h b/src/include/port.h index 30b6378ae5..10dcb5f0a6 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -525,11 +525,6 @@ extern int pg_mkdir_p(char *path, int omode); /* port/pqsignal.c */ typedef void (*pqsigfunc) (int signo); extern pqsigfunc pqsignal(int signo, pqsigfunc func); -#ifndef WIN32 -extern pqsigfunc pqsignal_no_restart(int signo, pqsigfunc func); -#else -#define pqsignal_no_restart(signo, func) pqsignal(signo, func) -#endif /* port/quotes.c */ extern char *escape_single_quotes_ascii(const char *src); diff --git a/src/port/pqsignal.c b/src/port/pqsignal.c index ecb9ca261f..814d616399 100644 --- a/src/port/pqsignal.c +++ b/src/port/pqsignal.c @@ -58,33 +58,4 @@ pqsignal(int signo, pqsigfunc func) #endif } -/* - * Set up a signal handler, without SA_RESTART, for signal "signo" - * - * Returns the previous handler. - * - * On Windows, this would be identical to pqsignal(), so don't bother. - */ -#ifndef WIN32 - -pqsigfunc -pqsignal_no_restart(int signo, pqsigfunc func) -{ - struct sigaction act, - oact; - - act.sa_handler = func; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; -#ifdef SA_NOCLDSTOP - if (signo == SIGCHLD) - act.sa_flags |= SA_NOCLDSTOP; -#endif - if (sigaction(signo, &act, &oact) < 0) - return SIG_ERR; - return oact.sa_handler; -} - -#endif /* !WIN32 */ - #endif /* !defined(WIN32) || defined(FRONTEND) */