Skip to content

Commit cac1de6

Browse files
hlinnakaCommitfest Bot
authored andcommitted
Use INTERRUPT_GENERAL for bgworker state change notification
As part of a project to remove the reliance on PIDs to identify backends, backends that register dynamic background workers will now receive INTERRUPT_GENERAL sent directly by the postmaster when the worker state changes. Previously, the postmaster would send a SIGUSR1 signal, which relied on the ProcSignal system's handler setting the interrupt flag, with the same end effect. Now that intermediate step is skipped. The field bgw_notify_pid still exists for backwards compatibility, but has no effect and may be removed in a later release. RegisterBackgroundWorker() now automatically assumes that the caller would like state change notifications delivered to its proc number, unless BGW_NO_NOTIFY is set in bgw_flags. Removing other cases of PIDs from the bgworker API is left for later work; this patch is concerned only with removing a dependency on ProcSignal infrastructure that is due to be removed by a later commit. This represents the first use of SendInterrupt() in the postmaster. It might seem more natural to use condition variables in the wait-for-state-change functions, so that anyone with a handle can wait, but condition variables as currently implemented would be a lot less robust than simple interrupts. Author: Thomas Munro <thomas.munro@gmail.com> Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi> Discussion: https://postgr.es/m/CA%2BhUKG%2B3MkS21yK4jL4cgZywdnnGKiBg0jatoV6kzaniBmcqbQ%40mail.gmail.com
1 parent 532d934 commit cac1de6

File tree

14 files changed

+112
-99
lines changed

14 files changed

+112
-99
lines changed

contrib/pg_prewarm/autoprewarm.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -817,9 +817,6 @@ apw_start_leader_worker(void)
817817
return;
818818
}
819819

820-
/* must set notify PID to wait for startup */
821-
worker.bgw_notify_pid = MyProcPid;
822-
823820
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
824821
ereport(ERROR,
825822
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
@@ -853,9 +850,6 @@ apw_start_database_worker(void)
853850
strcpy(worker.bgw_name, "autoprewarm worker");
854851
strcpy(worker.bgw_type, "autoprewarm worker");
855852

856-
/* must set notify PID to wait for shutdown */
857-
worker.bgw_notify_pid = MyProcPid;
858-
859853
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
860854
ereport(ERROR,
861855
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),

doc/src/sgml/bgworker.sgml

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ typedef struct BackgroundWorker
6363
char bgw_function_name[BGW_MAXLEN];
6464
Datum bgw_main_arg;
6565
char bgw_extra[BGW_EXTRALEN];
66-
pid_t bgw_notify_pid;
66+
pid_t bgw_notify_pid; /* not used */
6767
} BackgroundWorker;
6868
</programlisting>
6969
</para>
@@ -108,6 +108,18 @@ typedef struct BackgroundWorker
108108
</listitem>
109109
</varlistentry>
110110

111+
<varlistentry>
112+
<term><literal>BGWORKER_NO_NOTIFY</literal></term>
113+
<listitem>
114+
<para>
115+
<indexterm><primary>BGWORKER_NO_NOTIFY</primary></indexterm>
116+
Normally, the backend that registers a dynamic worker will be notified
117+
with <literal>INTERRUPT_GENERAL</literal> when the workers state changes, which allows the
118+
caller to wait for the worker to start and shut down. That can be
119+
suppressed by setting this flag.
120+
</para>
121+
</listitem>
122+
</varlistentry>
111123
</variablelist>
112124

113125
</para>
@@ -181,12 +193,8 @@ typedef struct BackgroundWorker
181193
</para>
182194

183195
<para>
184-
<structfield>bgw_notify_pid</structfield> is the PID of a PostgreSQL
185-
backend process to which the postmaster should send <literal>SIGUSR1</literal>
186-
when the process is started or exits. It should be 0 for workers registered
187-
at postmaster startup time, or when the backend registering the worker does
188-
not wish to wait for the worker to start up. Otherwise, it should be
189-
initialized to <literal>MyProcPid</literal>.
196+
<structfield>bgw_notify_pid</structfield> is not used and may be removed
197+
in a future release.
190198
</para>
191199

192200
<para>Once running, the process can connect to a database by calling
@@ -258,10 +266,7 @@ typedef struct BackgroundWorker
258266

259267
<para>
260268
In some cases, a process which registers a background worker may wish to
261-
wait for the worker to start up. This can be accomplished by initializing
262-
<structfield>bgw_notify_pid</structfield> to <literal>MyProcPid</literal> and
263-
then passing the <type>BackgroundWorkerHandle *</type> obtained at
264-
registration time to
269+
wait for the worker to start up. This can be accomplished with the
265270
<function>WaitForBackgroundWorkerStartup(<parameter>BackgroundWorkerHandle
266271
*handle</parameter>, <parameter>pid_t *</parameter>)</function> function.
267272
This function will block until the postmaster has attempted to start the

src/backend/access/transam/parallel.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,6 @@ LaunchParallelWorkers(ParallelContext *pcxt)
600600
sprintf(worker.bgw_library_name, "postgres");
601601
sprintf(worker.bgw_function_name, "ParallelWorkerMain");
602602
worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(pcxt->seg));
603-
worker.bgw_notify_pid = MyProcPid;
604603

605604
/*
606605
* Start workers.

src/backend/postmaster/bgworker.c

Lines changed: 68 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ typedef struct BackgroundWorkerSlot
7878
pid_t pid; /* InvalidPid = not started yet; 0 = dead */
7979
uint64 generation; /* incremented when slot is recycled */
8080
BackgroundWorker worker;
81+
int notify_pmchild;
82+
ProcNumber notify_proc_number;
8183
} BackgroundWorkerSlot;
8284

8385
/*
@@ -192,8 +194,9 @@ BackgroundWorkerShmemInit(void)
192194
slot->terminate = false;
193195
slot->pid = InvalidPid;
194196
slot->generation = 0;
197+
slot->notify_pmchild = 0;
198+
slot->notify_proc_number = INVALID_PROC_NUMBER;
195199
rw->rw_shmem_slot = slotno;
196-
rw->rw_worker.bgw_notify_pid = 0; /* might be reinit after crash */
197200
memcpy(&slot->worker, &rw->rw_worker, sizeof(BackgroundWorker));
198201
++slotno;
199202
}
@@ -234,6 +237,14 @@ FindRegisteredWorkerBySlotNumber(int slotno)
234237
return NULL;
235238
}
236239

240+
ProcNumber
241+
GetNotifyProcNumberForRegisteredWorker(RegisteredBgWorker *rw)
242+
{
243+
BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
244+
245+
return slot->notify_proc_number;
246+
}
247+
237248
/*
238249
* Notice changes to shared memory made by other backends.
239250
* Accept new worker requests only if allow_new_workers is true.
@@ -315,29 +326,29 @@ BackgroundWorkerStateChange(bool allow_new_workers)
315326
/*
316327
* If the worker is marked for termination, we don't need to add it to
317328
* the registered workers list; we can just free the slot. However, if
318-
* bgw_notify_pid is set, the process that registered the worker may
319-
* need to know that we've processed the terminate request, so be sure
320-
* to signal it.
329+
* bgw_notify_proc_number is set, the process that registered the
330+
* worker may need to know that we've processed the terminate request,
331+
* so be sure to signal it.
321332
*/
322333
if (slot->terminate)
323334
{
324-
int notify_pid;
335+
int notify_proc_number;
325336

326337
/*
327338
* We need a memory barrier here to make sure that the load of
328-
* bgw_notify_pid and the update of parallel_terminate_count
329-
* complete before the store to in_use.
339+
* bgw_notify_proc_number and the update of
340+
* parallel_terminate_count complete before the store to in_use.
330341
*/
331-
notify_pid = slot->worker.bgw_notify_pid;
342+
notify_proc_number = slot->notify_proc_number;
332343
if ((slot->worker.bgw_flags & BGWORKER_CLASS_PARALLEL) != 0)
333344
BackgroundWorkerData->parallel_terminate_count++;
334345
slot->pid = 0;
335346

336347
pg_memory_barrier();
337348
slot->in_use = false;
338349

339-
if (notify_pid != 0)
340-
kill(notify_pid, SIGUSR1);
350+
if (notify_proc_number != INVALID_PROC_NUMBER)
351+
SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
341352

342353
continue;
343354
}
@@ -383,23 +394,6 @@ BackgroundWorkerStateChange(bool allow_new_workers)
383394
rw->rw_worker.bgw_main_arg = slot->worker.bgw_main_arg;
384395
memcpy(rw->rw_worker.bgw_extra, slot->worker.bgw_extra, BGW_EXTRALEN);
385396

386-
/*
387-
* Copy the PID to be notified about state changes, but only if the
388-
* postmaster knows about a backend with that PID. It isn't an error
389-
* if the postmaster doesn't know about the PID, because the backend
390-
* that requested the worker could have died (or been killed) just
391-
* after doing so. Nonetheless, at least until we get some experience
392-
* with how this plays out in the wild, log a message at a relative
393-
* high debug level.
394-
*/
395-
rw->rw_worker.bgw_notify_pid = slot->worker.bgw_notify_pid;
396-
if (!PostmasterMarkPIDForWorkerNotify(rw->rw_worker.bgw_notify_pid))
397-
{
398-
elog(DEBUG1, "worker notification PID %d is not valid",
399-
(int) rw->rw_worker.bgw_notify_pid);
400-
rw->rw_worker.bgw_notify_pid = 0;
401-
}
402-
403397
/* Initialize postmaster bookkeeping. */
404398
rw->rw_pid = 0;
405399
rw->rw_crashed_at = 0;
@@ -421,7 +415,7 @@ BackgroundWorkerStateChange(bool allow_new_workers)
421415
* NOTE: The entry is unlinked from BackgroundWorkerList. If the caller is
422416
* iterating through it, better use a mutable iterator!
423417
*
424-
* Caller is responsible for notifying bgw_notify_pid, if appropriate.
418+
* Caller is responsible for notifying bgw_notify_proc_number, if appropriate.
425419
*
426420
* This function must be invoked only in the postmaster.
427421
*/
@@ -466,8 +460,8 @@ ReportBackgroundWorkerPID(RegisteredBgWorker *rw)
466460
slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
467461
slot->pid = rw->rw_pid;
468462

469-
if (rw->rw_worker.bgw_notify_pid != 0)
470-
kill(rw->rw_worker.bgw_notify_pid, SIGUSR1);
463+
if (slot->notify_proc_number != INVALID_PROC_NUMBER)
464+
SendInterrupt(INTERRUPT_GENERAL, slot->notify_proc_number);
471465
}
472466

473467
/*
@@ -483,12 +477,12 @@ void
483477
ReportBackgroundWorkerExit(RegisteredBgWorker *rw)
484478
{
485479
BackgroundWorkerSlot *slot;
486-
int notify_pid;
480+
ProcNumber notify_proc_number;
487481

488482
Assert(rw->rw_shmem_slot < max_worker_processes);
489483
slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
490484
slot->pid = rw->rw_pid;
491-
notify_pid = rw->rw_worker.bgw_notify_pid;
485+
notify_proc_number = slot->notify_proc_number;
492486

493487
/*
494488
* If this worker is slated for deregistration, do that before notifying
@@ -501,27 +495,34 @@ ReportBackgroundWorkerExit(RegisteredBgWorker *rw)
501495
rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART)
502496
ForgetBackgroundWorker(rw);
503497

504-
if (notify_pid != 0)
505-
kill(notify_pid, SIGUSR1);
498+
if (notify_proc_number != INVALID_PROC_NUMBER)
499+
SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
506500
}
507501

508502
/*
509-
* Cancel SIGUSR1 notifications for a PID belonging to an exiting backend.
503+
* Cancel notifications for a PID belonging to an exiting backend.
510504
*
511505
* This function should only be called from the postmaster.
512506
*/
513507
void
514-
BackgroundWorkerStopNotifications(pid_t pid)
508+
BackgroundWorkerStopNotifications(int pmchild)
515509
{
516510
dlist_iter iter;
517511

518512
dlist_foreach(iter, &BackgroundWorkerList)
519513
{
520514
RegisteredBgWorker *rw;
515+
BackgroundWorkerSlot *slot;
521516

522517
rw = dlist_container(RegisteredBgWorker, rw_lnode, iter.cur);
523-
if (rw->rw_worker.bgw_notify_pid == pid)
524-
rw->rw_worker.bgw_notify_pid = 0;
518+
Assert(rw->rw_shmem_slot < max_worker_processes);
519+
slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
520+
521+
if (slot->notify_pmchild == pmchild)
522+
{
523+
slot->notify_pmchild = 0;
524+
slot->notify_proc_number = INVALID_PROC_NUMBER;
525+
}
525526
}
526527
}
527528

@@ -553,14 +554,14 @@ ForgetUnstartedBackgroundWorkers(void)
553554

554555
/* If it's not yet started, and there's someone waiting ... */
555556
if (slot->pid == InvalidPid &&
556-
rw->rw_worker.bgw_notify_pid != 0)
557+
slot->notify_proc_number != INVALID_PROC_NUMBER)
557558
{
558559
/* ... then zap it, and notify the waiter */
559-
int notify_pid = rw->rw_worker.bgw_notify_pid;
560+
int notify_proc_number = slot->notify_proc_number;
560561

561562
ForgetBackgroundWorker(rw);
562-
if (notify_pid != 0)
563-
kill(notify_pid, SIGUSR1);
563+
if (notify_proc_number != INVALID_PROC_NUMBER)
564+
SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
564565
}
565566
}
566567
}
@@ -613,11 +614,6 @@ ResetBackgroundWorkerCrashTimes(void)
613614
* resetting.
614615
*/
615616
rw->rw_crashed_at = 0;
616-
617-
/*
618-
* If there was anyone waiting for it, they're history.
619-
*/
620-
rw->rw_worker.bgw_notify_pid = 0;
621617
}
622618
}
623619
}
@@ -981,15 +977,6 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
981977
if (!SanityCheckBackgroundWorker(worker, LOG))
982978
return;
983979

984-
if (worker->bgw_notify_pid != 0)
985-
{
986-
ereport(LOG,
987-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
988-
errmsg("background worker \"%s\": only dynamic background workers can request notification",
989-
worker->bgw_name)));
990-
return;
991-
}
992-
993980
/*
994981
* Enforce maximum number of workers. Note this is overly restrictive: we
995982
* could allow more non-shmem-connected workers, because these don't count
@@ -1105,6 +1092,25 @@ RegisterDynamicBackgroundWorker(BackgroundWorker *worker,
11051092
if (parallel)
11061093
BackgroundWorkerData->parallel_register_count++;
11071094

1095+
if ((slot->worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0 &&
1096+
(slot->worker.bgw_flags & BGWORKER_NO_NOTIFY) == 0)
1097+
{
1098+
/*
1099+
* Set notify_proc_number so that postmaster will send us an
1100+
* interrupt. Also remember the pmchild slot number;
1101+
* postmaster needs it to detect when we exit, to disarm the
1102+
* notification.
1103+
*/
1104+
slot->notify_pmchild = MyPMChildSlot;
1105+
slot->notify_proc_number = MyProcNumber;
1106+
}
1107+
else
1108+
{
1109+
/* No notifications. */
1110+
slot->notify_pmchild = 0;
1111+
slot->notify_proc_number = INVALID_PROC_NUMBER;
1112+
}
1113+
11081114
/*
11091115
* Make sure postmaster doesn't see the slot as in use before it
11101116
* sees the new contents.
@@ -1205,8 +1211,9 @@ GetBackgroundWorkerPid(BackgroundWorkerHandle *handle, pid_t *pidp)
12051211
* BGWH_POSTMASTER_DIED, since it that case we know that startup will not
12061212
* take place.
12071213
*
1208-
* The caller *must* have set our PID as the worker's bgw_notify_pid,
1209-
* else we will not be awoken promptly when the worker's state changes.
1214+
* This works only if the worker was registered with BGWORKER_SHMEM_ACCESS and
1215+
* without BGWORKER_NO_NOTIFY, else we will not be awoken promptly when the
1216+
* worker's state changes.
12101217
*/
12111218
BgwHandleStatus
12121219
WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
@@ -1250,8 +1257,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
12501257
* up and return BGWH_POSTMASTER_DIED, because it's the postmaster that
12511258
* notifies us when a worker's state changes.
12521259
*
1253-
* The caller *must* have set our PID as the worker's bgw_notify_pid,
1254-
* else we will not be awoken promptly when the worker's state changes.
1260+
* This works only if the worker was registered with BGWORKER_SHMEM_ACCESS and
1261+
* without BGWORKER_NO_NOTIFY, else we will not be awoken promptly when the
1262+
* worker's state changes.
12551263
*/
12561264
BgwHandleStatus
12571265
WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)

src/backend/postmaster/postmaster.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4008,15 +4008,15 @@ maybe_start_bgworkers(void)
40084008
{
40094009
if (rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART)
40104010
{
4011-
int notify_pid;
4011+
ProcNumber notify_proc_number;
40124012

4013-
notify_pid = rw->rw_worker.bgw_notify_pid;
4013+
notify_proc_number = GetNotifyProcNumberForRegisteredWorker(rw);
40144014

40154015
ForgetBackgroundWorker(rw);
40164016

40174017
/* Report worker is gone now. */
4018-
if (notify_pid != 0)
4019-
kill(notify_pid, SIGUSR1);
4018+
if (notify_proc_number != INVALID_PROC_NUMBER)
4019+
SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
40204020

40214021
continue;
40224022
}

src/backend/replication/logical/launcher.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,6 @@ logicalrep_worker_launch(LogicalRepWorkerType wtype,
496496
}
497497

498498
bgw.bgw_restart_time = BGW_NEVER_RESTART;
499-
bgw.bgw_notify_pid = MyProcPid;
500499
bgw.bgw_main_arg = Int32GetDatum(slot);
501500

502501
if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
@@ -939,7 +938,6 @@ ApplyLauncherRegister(void)
939938
snprintf(bgw.bgw_type, BGW_MAXLEN,
940939
"logical replication launcher");
941940
bgw.bgw_restart_time = 5;
942-
bgw.bgw_notify_pid = 0;
943941
bgw.bgw_main_arg = (Datum) 0;
944942

945943
RegisterBackgroundWorker(&bgw);

src/backend/storage/ipc/interrupt.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ RaiseInterrupt(InterruptType reason)
102102

103103
/*
104104
* Set an interrupt flag in another backend.
105+
*
106+
* Note: This can also be called from the postmaster, so be careful to not
107+
* trust the contents of shared memory.
105108
*/
106109
void
107110
SendInterrupt(InterruptType reason, ProcNumber pgprocno)

0 commit comments

Comments
 (0)