Use standard die() handler for SIGTERM in bgworkers

The previous default bgworker_die() signal would exit with elog(FATAL)
directly from the signal handler. That could cause deadlocks or
crashes if the signal handler runs while we're e.g holding a spinlock
or in the middle of a memory allocation.

All the built-in background workers overrode that to use the normal
die() handler and CHECK_FOR_INTERRUPTS(). Let's make that the default
for all background workers. Some extensions relying on the old
behavior might need to adapt, but the new default is much safer and is
the right thing to do for most background workers.

Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://www.postgresql.org/message-id/5238fe45-e486-4c62-a7f3-c7d8d416e812@iki.fi
This commit is contained in:
Heikki Linnakangas 2026-02-18 19:59:34 +02:00
parent 3894f08abe
commit d62dca3b29
7 changed files with 12 additions and 26 deletions

View file

@ -232,6 +232,8 @@ typedef struct BackgroundWorker
</para>
<para>
A well-behaved background worker must react promptly to standard signals
that the postmaster uses to control its child processes.
Signals are initially blocked when control reaches the
background worker's main function, and must be unblocked by it; this is to
allow the process to customize its signal handlers, if necessary.
@ -240,6 +242,14 @@ typedef struct BackgroundWorker
<function>BackgroundWorkerBlockSignals</function>.
</para>
<para>
The default signal handlers merely set interrupt flags
that are processed later by <function>CHECK_FOR_INTERRUPTS()</function>.
<function>CHECK_FOR_INTERRUPTS()</function> should be called in any
long-running loop to ensure that the background worker doesn't prevent the
system from shutting down in a timely fashion.
</para>
<para>
If <structfield>bgw_restart_time</structfield> for a background worker is
configured as <literal>BGW_NEVER_RESTART</literal>, or if it exits with an exit

View file

@ -1327,7 +1327,6 @@ ParallelWorkerMain(Datum main_arg)
InitializingParallelWorker = true;
/* Establish signal handlers. */
pqsignal(SIGTERM, die);
BackgroundWorkerUnblockSignals();
/* Determine and set our parallel worker number. */

View file

@ -718,20 +718,6 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel)
return true;
}
/*
* Standard SIGTERM handler for background workers
*/
static void
bgworker_die(SIGNAL_ARGS)
{
sigprocmask(SIG_SETMASK, &BlockSig, NULL);
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating background worker \"%s\" due to administrator command",
MyBgworkerEntry->bgw_type)));
}
/*
* Main entry point for background worker processes.
*/
@ -787,7 +773,7 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGFPE, SIG_IGN);
}
pqsignal(SIGTERM, bgworker_die);
pqsignal(SIGTERM, die);
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGHUP, SIG_IGN);

View file

@ -879,7 +879,6 @@ ParallelApplyWorkerMain(Datum main_arg)
* receiving SIGTERM.
*/
pqsignal(SIGHUP, SignalHandlerForConfigReload);
pqsignal(SIGTERM, die);
pqsignal(SIGUSR2, SignalHandlerForShutdownRequest);
BackgroundWorkerUnblockSignals();

View file

@ -1213,7 +1213,6 @@ ApplyLauncherMain(Datum main_arg)
/* Establish signal handlers. */
pqsignal(SIGHUP, SignalHandlerForConfigReload);
pqsignal(SIGTERM, die);
BackgroundWorkerUnblockSignals();
/*

View file

@ -5890,7 +5890,6 @@ SetupApplyOrSyncWorker(int worker_slot)
/* Setup signal handling */
pqsignal(SIGHUP, SignalHandlerForConfigReload);
pqsignal(SIGTERM, die);
BackgroundWorkerUnblockSignals();
/*

View file

@ -54,13 +54,7 @@ test_shm_mq_main(Datum main_arg)
int myworkernumber;
PGPROC *registrant;
/*
* Establish signal handlers.
*
* We want CHECK_FOR_INTERRUPTS() to kill off this worker process just as
* it would a normal user backend. To make that happen, we use die().
*/
pqsignal(SIGTERM, die);
/* Unblock signals. The standard signal handlers are OK for us. */
BackgroundWorkerUnblockSignals();
/*