]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Use standard die() handler for SIGTERM in bgworkers
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 18 Feb 2026 17:59:34 +0000 (19:59 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 18 Feb 2026 17:59:34 +0000 (19:59 +0200)
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

doc/src/sgml/bgworker.sgml
src/backend/access/transam/parallel.c
src/backend/postmaster/bgworker.c
src/backend/replication/logical/applyparallelworker.c
src/backend/replication/logical/launcher.c
src/backend/replication/logical/worker.c
src/test/modules/test_shm_mq/worker.c

index 4699ef6345fb5bbcb96137931472dad8d3bb7fea..2affba743825f1a0fc533dd93a5a19f0fd87bd49 100644 (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
index fe00488487db2684710f0ef513526eac6a9343a9..44786dc131f71912f82be5075e76408c0a0e21cb 100644 (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. */
index 261ccd3f59cfa15820c9a0a4dcc55842baa9703d..8678ea4e139ca45becf8dd17e444b478ccd87486 100644 (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);
 
index 8a01f16a2ca880acc49bc05c48881e672b4abefb..1730ace54902b89b60a28b3980bf4a266177b0fd 100644 (file)
@@ -879,7 +879,6 @@ ParallelApplyWorkerMain(Datum main_arg)
         * receiving SIGTERM.
         */
        pqsignal(SIGHUP, SignalHandlerForConfigReload);
-       pqsignal(SIGTERM, die);
        pqsignal(SIGUSR2, SignalHandlerForShutdownRequest);
        BackgroundWorkerUnblockSignals();
 
index 3ed86480be2b88d401efbde955d610bd29b0c6fb..e6112e11ec2746c35277eff9f6bcf0a6d15680d5 100644 (file)
@@ -1213,7 +1213,6 @@ ApplyLauncherMain(Datum main_arg)
 
        /* Establish signal handlers. */
        pqsignal(SIGHUP, SignalHandlerForConfigReload);
-       pqsignal(SIGTERM, die);
        BackgroundWorkerUnblockSignals();
 
        /*
index 8b93f48470c3300d06a66a419106c02308c558ea..f179d081846cad35faa36914fb7fbac109ecc7f3 100644 (file)
@@ -5890,7 +5890,6 @@ SetupApplyOrSyncWorker(int worker_slot)
 
        /* Setup signal handling */
        pqsignal(SIGHUP, SignalHandlerForConfigReload);
-       pqsignal(SIGTERM, die);
        BackgroundWorkerUnblockSignals();
 
        /*
index 368e4f3f23483f8b5bf035aa2ca3e6dc5d2398ed..6a4147554bb9bac71566c748b2ca9b240b1a219b 100644 (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();
 
        /*