Commit 969d7cd4 authored by Tom Lane's avatar Tom Lane

Install a "dead man switch" to allow the postmaster to detect cases where

a backend has done exit(0) or exit(1) without having disengaged itself
from shared memory.  We are at risk for this whenever third-party code is
loaded into a backend, since such code might not know it's supposed to go
through proc_exit() instead.  Also, it is reported that under Windows
there are ways to externally kill a process that cause the status code
returned to the postmaster to be indistinguishable from a voluntary exit
(thank you, Microsoft).  If this does happen then the system is probably
hosed --- for instance, the dead session might still be holding locks.
So the best recovery method is to treat this like a backend crash.

The dead man switch is armed for a particular child process when it
acquires a regular PGPROC, and disarmed when the PGPROC is released;
these should be the first and last touches of shared memory resources
in a backend, or close enough anyway.  This choice means there is no
coverage for auxiliary processes, but I doubt we need that, since they
shouldn't be executing any user-provided code anyway.

This patch also improves the management of the EXEC_BACKEND
ShmemBackendArray array a bit, by reducing search costs.

Although this problem is of long standing, the lack of field complaints
seems to mean it's not critical enough to risk back-patching; at least
not till we get some more testing of this mechanism.
parent 8f348112
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.580 2009/05/04 02:46:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.581 2009/05/05 19:59:00 tgl Exp $
* *
* NOTES * NOTES
* *
...@@ -135,12 +135,14 @@ ...@@ -135,12 +135,14 @@
* Also, "dead_end" children are in it: these are children launched just * Also, "dead_end" children are in it: these are children launched just
* for the purpose of sending a friendly rejection message to a would-be * for the purpose of sending a friendly rejection message to a would-be
* client. We must track them because they are attached to shared memory, * client. We must track them because they are attached to shared memory,
* but we know they will never become live backends. * but we know they will never become live backends. dead_end children are
* not assigned a PMChildSlot.
*/ */
typedef struct bkend typedef struct bkend
{ {
pid_t pid; /* process id of backend */ pid_t pid; /* process id of backend */
long cancel_key; /* cancel key for cancels for this backend */ long cancel_key; /* cancel key for cancels for this backend */
int child_slot; /* PMChildSlot for this backend, if any */
bool is_autovacuum; /* is it an autovacuum process? */ bool is_autovacuum; /* is it an autovacuum process? */
bool dead_end; /* is it going to send an error and quit? */ bool dead_end; /* is it going to send an error and quit? */
Dlelem elem; /* list link in BackendList */ Dlelem elem; /* list link in BackendList */
...@@ -149,15 +151,6 @@ typedef struct bkend ...@@ -149,15 +151,6 @@ typedef struct bkend
static Dllist *BackendList; static Dllist *BackendList;
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
/*
* Number of entries in the shared-memory backend table. This table is used
* only for sending cancels, and therefore only includes children we allow
* cancels on: regular backends and autovac workers. In particular we exclude
* dead_end children, allowing the table to have a known maximum size, to wit
* the same too-many-children limit enforced by canAcceptConnections().
*/
#define NUM_BACKENDARRAY_ELEMS (2*MaxBackends)
static Backend *ShmemBackendArray; static Backend *ShmemBackendArray;
#endif #endif
...@@ -404,6 +397,7 @@ typedef struct ...@@ -404,6 +397,7 @@ typedef struct
char DataDir[MAXPGPATH]; char DataDir[MAXPGPATH];
int ListenSocket[MAXLISTEN]; int ListenSocket[MAXLISTEN];
long MyCancelKey; long MyCancelKey;
int MyPMChildSlot;
unsigned long UsedShmemSegID; unsigned long UsedShmemSegID;
void *UsedShmemSegAddr; void *UsedShmemSegAddr;
slock_t *ShmemLock; slock_t *ShmemLock;
...@@ -413,6 +407,7 @@ typedef struct ...@@ -413,6 +407,7 @@ typedef struct
slock_t *ProcStructLock; slock_t *ProcStructLock;
PROC_HDR *ProcGlobal; PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs; PGPROC *AuxiliaryProcs;
PMSignalData *PMSignalState;
InheritableSocket pgStatSock; InheritableSocket pgStatSock;
pid_t PostmasterPid; pid_t PostmasterPid;
TimestampTz PgStartTime; TimestampTz PgStartTime;
...@@ -443,7 +438,7 @@ static bool save_backend_variables(BackendParameters * param, Port *port, ...@@ -443,7 +438,7 @@ static bool save_backend_variables(BackendParameters * param, Port *port,
#endif #endif
static void ShmemBackendArrayAdd(Backend *bn); static void ShmemBackendArrayAdd(Backend *bn);
static void ShmemBackendArrayRemove(pid_t pid); static void ShmemBackendArrayRemove(Backend *bn);
#endif /* EXEC_BACKEND */ #endif /* EXEC_BACKEND */
#define StartupDataBase() StartChildProcess(StartupProcess) #define StartupDataBase() StartChildProcess(StartupProcess)
...@@ -1771,7 +1766,7 @@ processCancelRequest(Port *port, void *pkt) ...@@ -1771,7 +1766,7 @@ processCancelRequest(Port *port, void *pkt)
{ {
bp = (Backend *) DLE_VAL(curr); bp = (Backend *) DLE_VAL(curr);
#else #else
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++) for (i = MaxLivePostmasterChildren() - 1; i >= 0; i--)
{ {
bp = (Backend *) &ShmemBackendArray[i]; bp = (Backend *) &ShmemBackendArray[i];
#endif #endif
...@@ -1836,10 +1831,10 @@ canAcceptConnections(void) ...@@ -1836,10 +1831,10 @@ canAcceptConnections(void)
* MaxBackends limit is enforced when a new backend tries to join the * MaxBackends limit is enforced when a new backend tries to join the
* shared-inval backend array. * shared-inval backend array.
* *
* In the EXEC_BACKEND case, the limit here must match the size of the * The limit here must match the sizes of the per-child-process arrays;
* ShmemBackendArray, since all these processes will have cancel codes. * see comments for MaxLivePostmasterChildren().
*/ */
if (CountChildren() >= 2 * MaxBackends) if (CountChildren() >= MaxLivePostmasterChildren())
return CAC_TOOMANY; return CAC_TOOMANY;
return CAC_OK; return CAC_OK;
...@@ -2439,8 +2434,8 @@ CleanupBackend(int pid, ...@@ -2439,8 +2434,8 @@ CleanupBackend(int pid,
/* /*
* If a backend dies in an ugly way then we must signal all other backends * If a backend dies in an ugly way then we must signal all other backends
* to quickdie. If exit status is zero (normal) or one (FATAL exit), we * to quickdie. If exit status is zero (normal) or one (FATAL exit), we
* assume everything is all right and simply remove the backend from the * assume everything is all right and proceed to remove the backend from
* active backend list. * the active backend list.
*/ */
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
{ {
...@@ -2454,10 +2449,21 @@ CleanupBackend(int pid, ...@@ -2454,10 +2449,21 @@ CleanupBackend(int pid,
if (bp->pid == pid) if (bp->pid == pid)
{ {
#ifdef EXEC_BACKEND
if (!bp->dead_end) if (!bp->dead_end)
ShmemBackendArrayRemove(pid); {
if (!ReleasePostmasterChildSlot(bp->child_slot))
{
/*
* Uh-oh, the child failed to clean itself up. Treat
* as a crash after all.
*/
HandleChildCrash(pid, exitstatus, _("server process"));
return;
}
#ifdef EXEC_BACKEND
ShmemBackendArrayRemove(bp);
#endif #endif
}
DLRemove(curr); DLRemove(curr);
free(bp); free(bp);
break; break;
...@@ -2500,10 +2506,13 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) ...@@ -2500,10 +2506,13 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
/* /*
* Found entry for freshly-dead backend, so remove it. * Found entry for freshly-dead backend, so remove it.
*/ */
#ifdef EXEC_BACKEND
if (!bp->dead_end) if (!bp->dead_end)
ShmemBackendArrayRemove(pid); {
(void) ReleasePostmasterChildSlot(bp->child_slot);
#ifdef EXEC_BACKEND
ShmemBackendArrayRemove(bp);
#endif #endif
}
DLRemove(curr); DLRemove(curr);
free(bp); free(bp);
/* Keep looping so we can signal remaining backends */ /* Keep looping so we can signal remaining backends */
...@@ -2931,14 +2940,7 @@ BackendStartup(Port *port) ...@@ -2931,14 +2940,7 @@ BackendStartup(Port *port)
pid_t pid; pid_t pid;
/* /*
* Compute the cancel key that will be assigned to this backend. The * Create backend data structure. Better before the fork() so we
* backend will have its own copy in the forked-off process' value of
* MyCancelKey, so that it can transmit the key to the frontend.
*/
MyCancelKey = PostmasterRandom();
/*
* Make room for backend data structure. Better before the fork() so we
* can handle failure cleanly. * can handle failure cleanly.
*/ */
bn = (Backend *) malloc(sizeof(Backend)); bn = (Backend *) malloc(sizeof(Backend));
...@@ -2950,8 +2952,26 @@ BackendStartup(Port *port) ...@@ -2950,8 +2952,26 @@ BackendStartup(Port *port)
return STATUS_ERROR; return STATUS_ERROR;
} }
/*
* Compute the cancel key that will be assigned to this backend. The
* backend will have its own copy in the forked-off process' value of
* MyCancelKey, so that it can transmit the key to the frontend.
*/
MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */ /* Pass down canAcceptConnections state */
port->canAcceptConnections = canAcceptConnections(); port->canAcceptConnections = canAcceptConnections();
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
port->canAcceptConnections != CAC_WAITBACKUP);
/*
* Unless it's a dead_end child, assign it a child slot number
*/
if (!bn->dead_end)
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
else
bn->child_slot = 0;
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
pid = backend_forkexec(port); pid = backend_forkexec(port);
...@@ -3009,10 +3029,7 @@ BackendStartup(Port *port) ...@@ -3009,10 +3029,7 @@ BackendStartup(Port *port)
* of backends. * of backends.
*/ */
bn->pid = pid; bn->pid = pid;
bn->cancel_key = MyCancelKey;
bn->is_autovacuum = false; bn->is_autovacuum = false;
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
port->canAcceptConnections != CAC_WAITBACKUP);
DLInitElem(&bn->elem, bn); DLInitElem(&bn->elem, bn);
DLAddHead(BackendList, &bn->elem); DLAddHead(BackendList, &bn->elem);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
...@@ -4271,23 +4288,26 @@ StartAutovacuumWorker(void) ...@@ -4271,23 +4288,26 @@ StartAutovacuumWorker(void)
*/ */
if (canAcceptConnections() == CAC_OK) if (canAcceptConnections() == CAC_OK)
{ {
/*
* Compute the cancel key that will be assigned to this session. We
* probably don't need cancel keys for autovac workers, but we'd
* better have something random in the field to prevent unfriendly
* people from sending cancels to them.
*/
MyCancelKey = PostmasterRandom();
bn = (Backend *) malloc(sizeof(Backend)); bn = (Backend *) malloc(sizeof(Backend));
if (bn) if (bn)
{ {
/*
* Compute the cancel key that will be assigned to this session. We
* probably don't need cancel keys for autovac workers, but we'd
* better have something random in the field to prevent unfriendly
* people from sending cancels to them.
*/
MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
/* Autovac workers are not dead_end and need a child slot */
bn->dead_end = false;
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
bn->pid = StartAutoVacWorker(); bn->pid = StartAutoVacWorker();
if (bn->pid > 0) if (bn->pid > 0)
{ {
bn->cancel_key = MyCancelKey;
bn->is_autovacuum = true; bn->is_autovacuum = true;
bn->dead_end = false;
DLInitElem(&bn->elem, bn); DLInitElem(&bn->elem, bn);
DLAddHead(BackendList, &bn->elem); DLAddHead(BackendList, &bn->elem);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
...@@ -4353,6 +4373,24 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname) ...@@ -4353,6 +4373,24 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname)
} }
/*
* MaxLivePostmasterChildren
*
* This reports the number of entries needed in per-child-process arrays
* (the PMChildFlags array, and if EXEC_BACKEND the ShmemBackendArray).
* These arrays include regular backends and autovac workers, but not special
* children nor dead_end children. This allows the arrays to have a fixed
* maximum size, to wit the same too-many-children limit enforced by
* canAcceptConnections(). The exact value isn't too critical as long as
* it's more than MaxBackends.
*/
int
MaxLivePostmasterChildren(void)
{
return 2 * MaxBackends;
}
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
/* /*
...@@ -4364,6 +4402,7 @@ extern LWLock *LWLockArray; ...@@ -4364,6 +4402,7 @@ extern LWLock *LWLockArray;
extern slock_t *ProcStructLock; extern slock_t *ProcStructLock;
extern PROC_HDR *ProcGlobal; extern PROC_HDR *ProcGlobal;
extern PGPROC *AuxiliaryProcs; extern PGPROC *AuxiliaryProcs;
extern PMSignalData *PMSignalState;
extern int pgStatSock; extern int pgStatSock;
#ifndef WIN32 #ifndef WIN32
...@@ -4395,6 +4434,7 @@ save_backend_variables(BackendParameters * param, Port *port, ...@@ -4395,6 +4434,7 @@ save_backend_variables(BackendParameters * param, Port *port,
memcpy(&param->ListenSocket, &ListenSocket, sizeof(ListenSocket)); memcpy(&param->ListenSocket, &ListenSocket, sizeof(ListenSocket));
param->MyCancelKey = MyCancelKey; param->MyCancelKey = MyCancelKey;
param->MyPMChildSlot = MyPMChildSlot;
param->UsedShmemSegID = UsedShmemSegID; param->UsedShmemSegID = UsedShmemSegID;
param->UsedShmemSegAddr = UsedShmemSegAddr; param->UsedShmemSegAddr = UsedShmemSegAddr;
...@@ -4407,6 +4447,7 @@ save_backend_variables(BackendParameters * param, Port *port, ...@@ -4407,6 +4447,7 @@ save_backend_variables(BackendParameters * param, Port *port,
param->ProcStructLock = ProcStructLock; param->ProcStructLock = ProcStructLock;
param->ProcGlobal = ProcGlobal; param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs; param->AuxiliaryProcs = AuxiliaryProcs;
param->PMSignalState = PMSignalState;
write_inheritable_socket(&param->pgStatSock, pgStatSock, childPid); write_inheritable_socket(&param->pgStatSock, pgStatSock, childPid);
param->PostmasterPid = PostmasterPid; param->PostmasterPid = PostmasterPid;
...@@ -4601,6 +4642,7 @@ restore_backend_variables(BackendParameters * param, Port *port) ...@@ -4601,6 +4642,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
memcpy(&ListenSocket, &param->ListenSocket, sizeof(ListenSocket)); memcpy(&ListenSocket, &param->ListenSocket, sizeof(ListenSocket));
MyCancelKey = param->MyCancelKey; MyCancelKey = param->MyCancelKey;
MyPMChildSlot = param->MyPMChildSlot;
UsedShmemSegID = param->UsedShmemSegID; UsedShmemSegID = param->UsedShmemSegID;
UsedShmemSegAddr = param->UsedShmemSegAddr; UsedShmemSegAddr = param->UsedShmemSegAddr;
...@@ -4613,6 +4655,7 @@ restore_backend_variables(BackendParameters * param, Port *port) ...@@ -4613,6 +4655,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
ProcStructLock = param->ProcStructLock; ProcStructLock = param->ProcStructLock;
ProcGlobal = param->ProcGlobal; ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs; AuxiliaryProcs = param->AuxiliaryProcs;
PMSignalState = param->PMSignalState;
read_inheritable_socket(&pgStatSock, &param->pgStatSock); read_inheritable_socket(&pgStatSock, &param->pgStatSock);
PostmasterPid = param->PostmasterPid; PostmasterPid = param->PostmasterPid;
...@@ -4642,7 +4685,7 @@ restore_backend_variables(BackendParameters * param, Port *port) ...@@ -4642,7 +4685,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
Size Size
ShmemBackendArraySize(void) ShmemBackendArraySize(void)
{ {
return mul_size(NUM_BACKENDARRAY_ELEMS, sizeof(Backend)); return mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
} }
void void
...@@ -4658,41 +4701,23 @@ ShmemBackendArrayAllocation(void) ...@@ -4658,41 +4701,23 @@ ShmemBackendArrayAllocation(void)
static void static void
ShmemBackendArrayAdd(Backend *bn) ShmemBackendArrayAdd(Backend *bn)
{ {
int i; /* The array slot corresponding to my PMChildSlot should be free */
int i = bn->child_slot - 1;
/* Find an empty slot */
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
{
if (ShmemBackendArray[i].pid == 0)
{
ShmemBackendArray[i] = *bn;
return;
}
}
ereport(FATAL, Assert(ShmemBackendArray[i].pid == 0);
(errmsg_internal("no free slots in shmem backend array"))); ShmemBackendArray[i] = *bn;
} }
static void static void
ShmemBackendArrayRemove(pid_t pid) ShmemBackendArrayRemove(Backend *bn)
{ {
int i; int i = bn->child_slot - 1;
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
{
if (ShmemBackendArray[i].pid == pid)
{
/* Mark the slot as empty */
ShmemBackendArray[i].pid = 0;
return;
}
}
ereport(WARNING, Assert(ShmemBackendArray[i].pid == bn->pid);
(errmsg_internal("could not find backend entry with pid %d", /* Mark the slot as empty */
(int) pid))); ShmemBackendArray[i].pid = 0;
} }
#endif /* EXEC_BACKEND */ #endif /* EXEC_BACKEND */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.99 2009/01/03 17:08:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.100 2009/05/05 19:59:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -111,6 +111,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) ...@@ -111,6 +111,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, ProcArrayShmemSize()); size = add_size(size, ProcArrayShmemSize());
size = add_size(size, BackendStatusShmemSize()); size = add_size(size, BackendStatusShmemSize());
size = add_size(size, SInvalShmemSize()); size = add_size(size, SInvalShmemSize());
size = add_size(size, PMSignalShmemSize());
size = add_size(size, BgWriterShmemSize()); size = add_size(size, BgWriterShmemSize());
size = add_size(size, AutoVacuumShmemSize()); size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, BTreeShmemSize()); size = add_size(size, BTreeShmemSize());
...@@ -206,7 +207,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) ...@@ -206,7 +207,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
/* /*
* Set up interprocess signaling mechanisms * Set up interprocess signaling mechanisms
*/ */
PMSignalInit(); PMSignalShmemInit();
BgWriterShmemInit(); BgWriterShmemInit();
AutoVacuumShmemInit(); AutoVacuumShmemInit();
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.26 2009/01/01 17:23:47 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.27 2009/05/05 19:59:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -34,26 +34,69 @@ ...@@ -34,26 +34,69 @@
* The flags are actually declared as "volatile sig_atomic_t" for maximum * The flags are actually declared as "volatile sig_atomic_t" for maximum
* portability. This should ensure that loads and stores of the flag * portability. This should ensure that loads and stores of the flag
* values are atomic, allowing us to dispense with any explicit locking. * values are atomic, allowing us to dispense with any explicit locking.
*
* In addition to the per-reason flags, we store a set of per-child-process
* flags that are currently used only for detecting whether a backend has
* exited without performing proper shutdown. The per-child-process flags
* have three possible states: UNUSED, ASSIGNED, ACTIVE. An UNUSED slot is
* available for assignment. An ASSIGNED slot is associated with a postmaster
* child process, but either the process has not touched shared memory yet,
* or it has successfully cleaned up after itself. A ACTIVE slot means the
* process is actively using shared memory. The slots are assigned to
* child processes at random, and postmaster.c is responsible for tracking
* which one goes with which PID.
*/
#define PM_CHILD_UNUSED 0 /* these values must fit in sig_atomic_t */
#define PM_CHILD_ASSIGNED 1
#define PM_CHILD_ACTIVE 2
/* "typedef struct PMSignalData PMSignalData" appears in pmsignal.h */
struct PMSignalData
{
/* per-reason flags */
sig_atomic_t PMSignalFlags[NUM_PMSIGNALS];
/* per-child-process flags */
int num_child_flags; /* # of entries in PMChildFlags[] */
int next_child_flag; /* next slot to try to assign */
sig_atomic_t PMChildFlags[1]; /* VARIABLE LENGTH ARRAY */
};
NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
/*
* PMSignalShmemSize
* Compute space needed for pmsignal.c's shared memory
*/ */
Size
PMSignalShmemSize(void)
{
Size size;
static volatile sig_atomic_t *PMSignalFlags; size = offsetof(PMSignalData, PMChildFlags);
size = add_size(size, mul_size(MaxLivePostmasterChildren(),
sizeof(sig_atomic_t)));
return size;
}
/* /*
* PMSignalInit - initialize during shared-memory creation * PMSignalShmemInit - initialize during shared-memory creation
*/ */
void void
PMSignalInit(void) PMSignalShmemInit(void)
{ {
bool found; bool found;
PMSignalFlags = (sig_atomic_t *) PMSignalState = (PMSignalData *)
ShmemInitStruct("PMSignalFlags", ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
NUM_PMSIGNALS * sizeof(sig_atomic_t),
&found);
if (!found) if (!found)
MemSet(PMSignalFlags, 0, NUM_PMSIGNALS * sizeof(sig_atomic_t)); {
MemSet(PMSignalState, 0, PMSignalShmemSize());
PMSignalState->num_child_flags = MaxLivePostmasterChildren();
}
} }
/* /*
...@@ -66,7 +109,7 @@ SendPostmasterSignal(PMSignalReason reason) ...@@ -66,7 +109,7 @@ SendPostmasterSignal(PMSignalReason reason)
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
return; return;
/* Atomically set the proper flag */ /* Atomically set the proper flag */
PMSignalFlags[reason] = true; PMSignalState->PMSignalFlags[reason] = true;
/* Send signal to postmaster */ /* Send signal to postmaster */
kill(PostmasterPid, SIGUSR1); kill(PostmasterPid, SIGUSR1);
} }
...@@ -80,14 +123,105 @@ bool ...@@ -80,14 +123,105 @@ bool
CheckPostmasterSignal(PMSignalReason reason) CheckPostmasterSignal(PMSignalReason reason)
{ {
/* Careful here --- don't clear flag if we haven't seen it set */ /* Careful here --- don't clear flag if we haven't seen it set */
if (PMSignalFlags[reason]) if (PMSignalState->PMSignalFlags[reason])
{ {
PMSignalFlags[reason] = false; PMSignalState->PMSignalFlags[reason] = false;
return true; return true;
} }
return false; return false;
} }
/*
* AssignPostmasterChildSlot - select an unused slot for a new postmaster
* child process, and set its state to ASSIGNED. Returns a slot number
* (one to N).
*
* Only the postmaster is allowed to execute this routine, so we need no
* special locking.
*/
int
AssignPostmasterChildSlot(void)
{
int slot = PMSignalState->next_child_flag;
int n;
/*
* Scan for a free slot. We track the last slot assigned so as not to
* waste time repeatedly rescanning low-numbered slots.
*/
for (n = PMSignalState->num_child_flags; n > 0; n--)
{
if (--slot < 0)
slot = PMSignalState->num_child_flags - 1;
if (PMSignalState->PMChildFlags[slot] == PM_CHILD_UNUSED)
{
PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
PMSignalState->next_child_flag = slot;
return slot + 1;
}
}
/* Out of slots ... should never happen, else postmaster.c messed up */
elog(FATAL, "no free slots in PMChildFlags array");
return 0; /* keep compiler quiet */
}
/*
* ReleasePostmasterChildSlot - release a slot after death of a postmaster
* child process. This must be called in the postmaster process.
*
* Returns true if the slot had been in ASSIGNED state (the expected case),
* false otherwise (implying that the child failed to clean itself up).
*/
bool
ReleasePostmasterChildSlot(int slot)
{
bool result;
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
slot--;
/*
* Note: the slot state might already be unused, because the logic in
* postmaster.c is such that this might get called twice when a child
* crashes. So we don't try to Assert anything about the state.
*/
result = (PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
PMSignalState->PMChildFlags[slot] = PM_CHILD_UNUSED;
return result;
}
/*
* MarkPostmasterChildActive - mark a postmaster child as about to begin
* actively using shared memory. This is called in the child process.
*/
void
MarkPostmasterChildActive(void)
{
int slot = MyPMChildSlot;
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
slot--;
Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
PMSignalState->PMChildFlags[slot] = PM_CHILD_ACTIVE;
}
/*
* MarkPostmasterChildInactive - mark a postmaster child as done using
* shared memory. This is called in the child process.
*/
void
MarkPostmasterChildInactive(void)
{
int slot = MyPMChildSlot;
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
slot--;
Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE);
PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
}
/* /*
* PostmasterIsAlive - check whether postmaster process is still alive * PostmasterIsAlive - check whether postmaster process is still alive
* *
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.205 2009/01/01 17:23:48 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.206 2009/05/05 19:59:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/spin.h" #include "storage/spin.h"
...@@ -274,6 +275,14 @@ InitProcess(void) ...@@ -274,6 +275,14 @@ InitProcess(void)
errmsg("sorry, too many clients already"))); errmsg("sorry, too many clients already")));
} }
/*
* Now that we have a PGPROC, mark ourselves as an active postmaster
* child; this is so that the postmaster can detect it if we exit
* without cleaning up.
*/
if (IsUnderPostmaster)
MarkPostmasterChildActive();
/* /*
* Initialize all fields of MyProc, except for the semaphore which was * Initialize all fields of MyProc, except for the semaphore which was
* prepared for us by InitProcGlobal. * prepared for us by InitProcGlobal.
...@@ -614,6 +623,13 @@ ProcKill(int code, Datum arg) ...@@ -614,6 +623,13 @@ ProcKill(int code, Datum arg)
SpinLockRelease(ProcStructLock); SpinLockRelease(ProcStructLock);
/*
* This process is no longer present in shared memory in any meaningful
* way, so tell the postmaster we've cleaned up acceptably well.
*/
if (IsUnderPostmaster)
MarkPostmasterChildInactive();
/* wake autovac launcher if needed -- see comments in FreeWorkerInfo */ /* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
if (AutovacuumLauncherPid != 0) if (AutovacuumLauncherPid != 0)
kill(AutovacuumLauncherPid, SIGUSR1); kill(AutovacuumLauncherPid, SIGUSR1);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.107 2009/01/01 17:23:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.108 2009/05/05 19:59:00 tgl Exp $
* *
* NOTES * NOTES
* Globals used all over the place should be declared here and not * Globals used all over the place should be declared here and not
...@@ -36,6 +36,7 @@ int MyProcPid; ...@@ -36,6 +36,7 @@ int MyProcPid;
pg_time_t MyStartTime; pg_time_t MyStartTime;
struct Port *MyProcPort; struct Port *MyProcPort;
long MyCancelKey; long MyCancelKey;
int MyPMChildSlot;
/* /*
* DataDir is the absolute path to the top level of the PGDATA directory tree. * DataDir is the absolute path to the top level of the PGDATA directory tree.
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.209 2009/01/05 02:27:45 alvherre Exp $ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.210 2009/05/05 19:59:00 tgl Exp $
* *
* NOTES * NOTES
* some of the information in this file should be moved to other files. * some of the information in this file should be moved to other files.
...@@ -137,6 +137,7 @@ extern PGDLLIMPORT int MyProcPid; ...@@ -137,6 +137,7 @@ extern PGDLLIMPORT int MyProcPid;
extern PGDLLIMPORT pg_time_t MyStartTime; extern PGDLLIMPORT pg_time_t MyStartTime;
extern PGDLLIMPORT struct Port *MyProcPort; extern PGDLLIMPORT struct Port *MyProcPort;
extern long MyCancelKey; extern long MyCancelKey;
extern int MyPMChildSlot;
extern char OutputFileName[]; extern char OutputFileName[];
extern PGDLLIMPORT char my_exec_path[]; extern PGDLLIMPORT char my_exec_path[];
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.19 2009/01/01 17:24:01 momjian Exp $ * $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.20 2009/05/05 19:59:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,8 @@ extern const char *progname; ...@@ -38,6 +38,8 @@ extern const char *progname;
extern int PostmasterMain(int argc, char *argv[]); extern int PostmasterMain(int argc, char *argv[]);
extern void ClosePostmasterPorts(bool am_syslogger); extern void ClosePostmasterPorts(bool am_syslogger);
extern int MaxLivePostmasterChildren(void);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
extern pid_t postmaster_forkexec(int argc, char *argv[]); extern pid_t postmaster_forkexec(int argc, char *argv[]);
extern int SubPostmasterMain(int argc, char *argv[]); extern int SubPostmasterMain(int argc, char *argv[]);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.23 2009/02/23 09:28:50 heikki Exp $ * $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.24 2009/05/05 19:59:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,12 +33,20 @@ typedef enum ...@@ -33,12 +33,20 @@ typedef enum
NUM_PMSIGNALS /* Must be last value of enum! */ NUM_PMSIGNALS /* Must be last value of enum! */
} PMSignalReason; } PMSignalReason;
/* PMSignalData is an opaque struct, details known only within pmsignal.c */
typedef struct PMSignalData PMSignalData;
/* /*
* prototypes for functions in pmsignal.c * prototypes for functions in pmsignal.c
*/ */
extern void PMSignalInit(void); extern Size PMSignalShmemSize(void);
extern void PMSignalShmemInit(void);
extern void SendPostmasterSignal(PMSignalReason reason); extern void SendPostmasterSignal(PMSignalReason reason);
extern bool CheckPostmasterSignal(PMSignalReason reason); extern bool CheckPostmasterSignal(PMSignalReason reason);
extern int AssignPostmasterChildSlot(void);
extern bool ReleasePostmasterChildSlot(int slot);
extern void MarkPostmasterChildActive(void);
extern void MarkPostmasterChildInactive(void);
extern bool PostmasterIsAlive(bool amDirectChild); extern bool PostmasterIsAlive(bool amDirectChild);
#endif /* PMSIGNAL_H */ #endif /* PMSIGNAL_H */
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