Commit 2dee7998 authored by Robert Haas's avatar Robert Haas

Move more bgworker code to bgworker.c; also, some renaming.

Per discussion on pgsql-hackers.

Michael Paquier, slightly modified by me.  Original suggestion
from Amit Kapila.
parent ac76ec27
...@@ -12,13 +12,24 @@ ...@@ -12,13 +12,24 @@
#include "postgres.h" #include "postgres.h"
#include <unistd.h>
#include <time.h>
#include "miscadmin.h" #include "miscadmin.h"
#include "libpq/pqsignal.h"
#include "postmaster/bgworker_internals.h" #include "postmaster/bgworker_internals.h"
#include "storage/barrier.h" #include "storage/barrier.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/shmem.h" #include "storage/shmem.h"
#include "tcop/tcopprot.h"
#include "utils/ascii.h" #include "utils/ascii.h"
#include "utils/ps_status.h"
#include "utils/timeout.h"
/* /*
* The postmaster's list of registered background workers, in private memory. * The postmaster's list of registered background workers, in private memory.
...@@ -354,6 +365,214 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel) ...@@ -354,6 +365,214 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel)
return true; return true;
} }
static void
bgworker_quickdie(SIGNAL_ARGS)
{
sigaddset(&BlockSig, SIGQUIT); /* prevent nested calls */
PG_SETMASK(&BlockSig);
/*
* We DO NOT want to run proc_exit() callbacks -- we're here because
* shared memory may be corrupted, so we don't want to try to clean up our
* transaction. Just nail the windows shut and get out of town. Now that
* there's an atexit callback to prevent third-party code from breaking
* things by calling exit() directly, we have to reset the callbacks
* explicitly to make this work as intended.
*/
on_exit_reset();
/*
* Note we do exit(0) here, not exit(2) like quickdie. The reason is that
* we don't want to be seen this worker as independently crashed, because
* then postmaster would delay restarting it again afterwards. If some
* idiot DBA manually sends SIGQUIT to a random bgworker, the "dead man
* switch" will ensure that postmaster sees this as a crash.
*/
exit(0);
}
/*
* Standard SIGTERM handler for background workers
*/
static void
bgworker_die(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating background worker \"%s\" due to administrator command",
MyBgworkerEntry->bgw_name)));
}
/*
* Standard SIGUSR1 handler for unconnected workers
*
* Here, we want to make sure an unconnected worker will at least heed
* latch activity.
*/
static void
bgworker_sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
latch_sigusr1_handler();
errno = save_errno;
}
/*
* Start a new background worker
*
* This is the main entry point for background worker, to be called from
* postmaster.
*/
void
StartBackgroundWorker(void)
{
sigjmp_buf local_sigjmp_buf;
char buf[MAXPGPATH];
BackgroundWorker *worker = MyBgworkerEntry;
bgworker_main_type entrypt;
if (worker == NULL)
elog(FATAL, "unable to find bgworker entry");
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
IsBackgroundWorker = true;
/* reset MyProcPid */
MyProcPid = getpid();
/* record Start Time for logging */
MyStartTime = time(NULL);
/* Identify myself via ps */
snprintf(buf, MAXPGPATH, "bgworker: %s", worker->bgw_name);
init_ps_display(buf, "", "", "");
SetProcessingMode(InitProcessing);
/* Apply PostAuthDelay */
if (PostAuthDelay > 0)
pg_usleep(PostAuthDelay * 1000000L);
/*
* If possible, make this process a group leader, so that the postmaster
* can signal any child processes too.
*/
#ifdef HAVE_SETSID
if (setsid() < 0)
elog(FATAL, "setsid() failed: %m");
#endif
/*
* Set up signal handlers.
*/
if (worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)
{
/*
* SIGINT is used to signal canceling the current action
*/
pqsignal(SIGINT, StatementCancelHandler);
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
pqsignal(SIGFPE, FloatExceptionHandler);
/* XXX Any other handlers needed here? */
}
else
{
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGUSR1, bgworker_sigusr1_handler);
pqsignal(SIGFPE, SIG_IGN);
}
pqsignal(SIGTERM, bgworker_die);
pqsignal(SIGHUP, SIG_IGN);
pqsignal(SIGQUIT, bgworker_quickdie);
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGCHLD, SIG_DFL);
/*
* If an exception is encountered, processing resumes here.
*
* See notes in postgres.c about the design of this coding.
*/
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/* Since not using PG_TRY, must reset error stack by hand */
error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Report the error to the server log */
EmitErrorReport();
/*
* Do we need more cleanup here? For shmem-connected bgworkers, we
* will call InitProcess below, which will install ProcKill as exit
* callback. That will take care of releasing locks, etc.
*/
/* and go away */
proc_exit(1);
}
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
/* Early initialization */
BaseInit();
/*
* If necessary, create a per-backend PGPROC struct in shared memory,
* except in the EXEC_BACKEND case where this was done in
* SubPostmasterMain. We must do this before we can use LWLocks (and in
* the EXEC_BACKEND case we already had to do some stuff with LWLocks).
*/
#ifndef EXEC_BACKEND
if (worker->bgw_flags & BGWORKER_SHMEM_ACCESS)
InitProcess();
#endif
/*
* If bgw_main is set, we use that value as the initial entrypoint.
* However, if the library containing the entrypoint wasn't loaded at
* postmaster startup time, passing it as a direct function pointer is
* not possible. To work around that, we allow callers for whom a
* function pointer is not available to pass a library name (which will
* be loaded, if necessary) and a function name (which will be looked up
* in the named library).
*/
if (worker->bgw_main != NULL)
entrypt = worker->bgw_main;
else
entrypt = (bgworker_main_type)
load_external_function(worker->bgw_library_name,
worker->bgw_function_name,
true, NULL);
/*
* Note that in normal processes, we would call InitPostgres here. For a
* worker, however, we don't know what database to connect to, yet; so we
* need to wait until the user code does it via
* BackgroundWorkerInitializeConnection().
*/
/*
* Now invoke the user-defined worker code
*/
entrypt(worker->bgw_main_arg);
/* ... and if it returns, we're done */
proc_exit(0);
}
/* /*
* Register a new background worker while processing shared_preload_libraries. * Register a new background worker while processing shared_preload_libraries.
* *
......
...@@ -383,7 +383,6 @@ static void dummy_handler(SIGNAL_ARGS); ...@@ -383,7 +383,6 @@ static void dummy_handler(SIGNAL_ARGS);
static void StartupPacketTimeoutHandler(void); static void StartupPacketTimeoutHandler(void);
static void CleanupBackend(int pid, int exitstatus); static void CleanupBackend(int pid, int exitstatus);
static bool CleanupBackgroundWorker(int pid, int exitstatus); static bool CleanupBackgroundWorker(int pid, int exitstatus);
static void do_start_bgworker(void);
static void HandleChildCrash(int pid, int exitstatus, const char *procname); static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname, static void LogChildExit(int lev, const char *procname,
int pid, int exitstatus); int pid, int exitstatus);
...@@ -409,7 +408,7 @@ static void TerminateChildren(int signal); ...@@ -409,7 +408,7 @@ static void TerminateChildren(int signal);
static int CountChildren(int target); static int CountChildren(int target);
static int CountUnconnectedWorkers(void); static int CountUnconnectedWorkers(void);
static void StartOneBackgroundWorker(void); static void maybe_start_bgworker(void);
static bool CreateOptsFile(int argc, char *argv[], char *fullprogname); static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
static pid_t StartChildProcess(AuxProcType type); static pid_t StartChildProcess(AuxProcType type);
static void StartAutovacuumWorker(void); static void StartAutovacuumWorker(void);
...@@ -1232,7 +1231,7 @@ PostmasterMain(int argc, char *argv[]) ...@@ -1232,7 +1231,7 @@ PostmasterMain(int argc, char *argv[])
pmState = PM_STARTUP; pmState = PM_STARTUP;
/* Some workers may be scheduled to start now */ /* Some workers may be scheduled to start now */
StartOneBackgroundWorker(); maybe_start_bgworker();
status = ServerLoop(); status = ServerLoop();
...@@ -1650,7 +1649,7 @@ ServerLoop(void) ...@@ -1650,7 +1649,7 @@ ServerLoop(void)
/* Get other worker processes running, if needed */ /* Get other worker processes running, if needed */
if (StartWorkerNeeded || HaveCrashedWorker) if (StartWorkerNeeded || HaveCrashedWorker)
StartOneBackgroundWorker(); maybe_start_bgworker();
/* /*
* Touch Unix socket and lock files every 58 minutes, to ensure that * Touch Unix socket and lock files every 58 minutes, to ensure that
...@@ -2610,7 +2609,7 @@ reaper(SIGNAL_ARGS) ...@@ -2610,7 +2609,7 @@ reaper(SIGNAL_ARGS)
PgStatPID = pgstat_start(); PgStatPID = pgstat_start();
/* some workers may be scheduled to start now */ /* some workers may be scheduled to start now */
StartOneBackgroundWorker(); maybe_start_bgworker();
/* at this point we are really open for business */ /* at this point we are really open for business */
ereport(LOG, ereport(LOG,
...@@ -4626,7 +4625,7 @@ SubPostmasterMain(int argc, char *argv[]) ...@@ -4626,7 +4625,7 @@ SubPostmasterMain(int argc, char *argv[])
shmem_slot = atoi(argv[1] + 15); shmem_slot = atoi(argv[1] + 15);
MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot); MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
do_start_bgworker(); StartBackgroundWorker();
} }
if (strcmp(argv[1], "--forkarch") == 0) if (strcmp(argv[1], "--forkarch") == 0)
{ {
...@@ -4741,7 +4740,7 @@ sigusr1_handler(SIGNAL_ARGS) ...@@ -4741,7 +4740,7 @@ sigusr1_handler(SIGNAL_ARGS)
} }
if (start_bgworker) if (start_bgworker)
StartOneBackgroundWorker(); maybe_start_bgworker();
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) && if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
PgArchPID != 0) PgArchPID != 0)
...@@ -5253,208 +5252,6 @@ BackgroundWorkerUnblockSignals(void) ...@@ -5253,208 +5252,6 @@ BackgroundWorkerUnblockSignals(void)
PG_SETMASK(&UnBlockSig); PG_SETMASK(&UnBlockSig);
} }
static void
bgworker_quickdie(SIGNAL_ARGS)
{
sigaddset(&BlockSig, SIGQUIT); /* prevent nested calls */
PG_SETMASK(&BlockSig);
/*
* We DO NOT want to run proc_exit() callbacks -- we're here because
* shared memory may be corrupted, so we don't want to try to clean up our
* transaction. Just nail the windows shut and get out of town. Now that
* there's an atexit callback to prevent third-party code from breaking
* things by calling exit() directly, we have to reset the callbacks
* explicitly to make this work as intended.
*/
on_exit_reset();
/*
* Note we do exit(0) here, not exit(2) like quickdie. The reason is that
* we don't want to be seen this worker as independently crashed, because
* then postmaster would delay restarting it again afterwards. If some
* idiot DBA manually sends SIGQUIT to a random bgworker, the "dead man
* switch" will ensure that postmaster sees this as a crash.
*/
exit(0);
}
/*
* Standard SIGTERM handler for background workers
*/
static void
bgworker_die(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating background worker \"%s\" due to administrator command",
MyBgworkerEntry->bgw_name)));
}
/*
* Standard SIGUSR1 handler for unconnected workers
*
* Here, we want to make sure an unconnected worker will at least heed
* latch activity.
*/
static void
bgworker_sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
latch_sigusr1_handler();
errno = save_errno;
}
static void
do_start_bgworker(void)
{
sigjmp_buf local_sigjmp_buf;
char buf[MAXPGPATH];
BackgroundWorker *worker = MyBgworkerEntry;
bgworker_main_type entrypt;
if (worker == NULL)
elog(FATAL, "unable to find bgworker entry");
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
IsBackgroundWorker = true;
/* reset MyProcPid */
MyProcPid = getpid();
/* record Start Time for logging */
MyStartTime = time(NULL);
/* Identify myself via ps */
snprintf(buf, MAXPGPATH, "bgworker: %s", worker->bgw_name);
init_ps_display(buf, "", "", "");
SetProcessingMode(InitProcessing);
/* Apply PostAuthDelay */
if (PostAuthDelay > 0)
pg_usleep(PostAuthDelay * 1000000L);
/*
* If possible, make this process a group leader, so that the postmaster
* can signal any child processes too.
*/
#ifdef HAVE_SETSID
if (setsid() < 0)
elog(FATAL, "setsid() failed: %m");
#endif
/*
* Set up signal handlers.
*/
if (worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)
{
/*
* SIGINT is used to signal canceling the current action
*/
pqsignal(SIGINT, StatementCancelHandler);
pqsignal(SIGUSR1, procsignal_sigusr1_handler);
pqsignal(SIGFPE, FloatExceptionHandler);
/* XXX Any other handlers needed here? */
}
else
{
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGUSR1, bgworker_sigusr1_handler);
pqsignal(SIGFPE, SIG_IGN);
}
pqsignal(SIGTERM, bgworker_die);
pqsignal(SIGHUP, SIG_IGN);
pqsignal(SIGQUIT, bgworker_quickdie);
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGCHLD, SIG_DFL);
/*
* If an exception is encountered, processing resumes here.
*
* See notes in postgres.c about the design of this coding.
*/
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/* Since not using PG_TRY, must reset error stack by hand */
error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Report the error to the server log */
EmitErrorReport();
/*
* Do we need more cleanup here? For shmem-connected bgworkers, we
* will call InitProcess below, which will install ProcKill as exit
* callback. That will take care of releasing locks, etc.
*/
/* and go away */
proc_exit(1);
}
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
/* Early initialization */
BaseInit();
/*
* If necessary, create a per-backend PGPROC struct in shared memory,
* except in the EXEC_BACKEND case where this was done in
* SubPostmasterMain. We must do this before we can use LWLocks (and in
* the EXEC_BACKEND case we already had to do some stuff with LWLocks).
*/
#ifndef EXEC_BACKEND
if (worker->bgw_flags & BGWORKER_SHMEM_ACCESS)
InitProcess();
#endif
/*
* If bgw_main is set, we use that value as the initial entrypoint.
* However, if the library containing the entrypoint wasn't loaded at
* postmaster startup time, passing it as a direct function pointer is
* not possible. To work around that, we allow callers for whom a
* function pointer is not available to pass a library name (which will
* be loaded, if necessary) and a function name (which will be looked up
* in the named library).
*/
if (worker->bgw_main != NULL)
entrypt = worker->bgw_main;
else
entrypt = (bgworker_main_type)
load_external_function(worker->bgw_library_name,
worker->bgw_function_name,
true, NULL);
/*
* Note that in normal processes, we would call InitPostgres here. For a
* worker, however, we don't know what database to connect to, yet; so we
* need to wait until the user code does it via
* BackgroundWorkerInitializeConnection().
*/
/*
* Now invoke the user-defined worker code
*/
entrypt(worker->bgw_main_arg);
/* ... and if it returns, we're done */
proc_exit(0);
}
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
static pid_t static pid_t
bgworker_forkexec(int shmem_slot) bgworker_forkexec(int shmem_slot)
...@@ -5483,7 +5280,7 @@ bgworker_forkexec(int shmem_slot) ...@@ -5483,7 +5280,7 @@ bgworker_forkexec(int shmem_slot)
* This code is heavily based on autovacuum.c, q.v. * This code is heavily based on autovacuum.c, q.v.
*/ */
static void static void
start_bgworker(RegisteredBgWorker *rw) do_start_bgworker(RegisteredBgWorker *rw)
{ {
pid_t worker_pid; pid_t worker_pid;
...@@ -5514,7 +5311,7 @@ start_bgworker(RegisteredBgWorker *rw) ...@@ -5514,7 +5311,7 @@ start_bgworker(RegisteredBgWorker *rw)
/* Do NOT release postmaster's working memory context */ /* Do NOT release postmaster's working memory context */
MyBgworkerEntry = &rw->rw_worker; MyBgworkerEntry = &rw->rw_worker;
do_start_bgworker(); StartBackgroundWorker();
break; break;
#endif #endif
default: default:
...@@ -5618,7 +5415,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw) ...@@ -5618,7 +5415,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
* system state requires it. * system state requires it.
*/ */
static void static void
StartOneBackgroundWorker(void) maybe_start_bgworker(void)
{ {
slist_mutable_iter iter; slist_mutable_iter iter;
TimestampTz now = 0; TimestampTz now = 0;
...@@ -5689,7 +5486,7 @@ StartOneBackgroundWorker(void) ...@@ -5689,7 +5486,7 @@ StartOneBackgroundWorker(void)
else else
rw->rw_child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); rw->rw_child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
start_bgworker(rw); /* sets rw->rw_pid */ do_start_bgworker(rw); /* sets rw->rw_pid */
if (rw->rw_backend) if (rw->rw_backend)
{ {
......
...@@ -41,6 +41,9 @@ extern void BackgroundWorkerShmemInit(void); ...@@ -41,6 +41,9 @@ extern void BackgroundWorkerShmemInit(void);
extern void BackgroundWorkerStateChange(void); extern void BackgroundWorkerStateChange(void);
extern void ForgetBackgroundWorker(slist_mutable_iter *cur); extern void ForgetBackgroundWorker(slist_mutable_iter *cur);
/* Function to start a background worker, called from postmaster.c */
extern void StartBackgroundWorker(void);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
extern BackgroundWorker *BackgroundWorkerEntry(int slotno); extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
#endif #endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment