*
*
* 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
*
* 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
* 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
{
pid_t pid; /* process id of 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 dead_end; /* is it going to send an error and quit? */
Dlelem elem; /* list link in BackendList */
static Dllist *BackendList;
#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;
#endif
char DataDir[MAXPGPATH];
int ListenSocket[MAXLISTEN];
long MyCancelKey;
+ int MyPMChildSlot;
unsigned long UsedShmemSegID;
void *UsedShmemSegAddr;
slock_t *ShmemLock;
slock_t *ProcStructLock;
PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs;
+ PMSignalData *PMSignalState;
InheritableSocket pgStatSock;
pid_t PostmasterPid;
TimestampTz PgStartTime;
#endif
static void ShmemBackendArrayAdd(Backend *bn);
-static void ShmemBackendArrayRemove(pid_t pid);
+static void ShmemBackendArrayRemove(Backend *bn);
#endif /* EXEC_BACKEND */
#define StartupDataBase() StartChildProcess(StartupProcess)
{
bp = (Backend *) DLE_VAL(curr);
#else
- for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
+ for (i = MaxLivePostmasterChildren() - 1; i >= 0; i--)
{
bp = (Backend *) &ShmemBackendArray[i];
#endif
* MaxBackends limit is enforced when a new backend tries to join the
* shared-inval backend array.
*
- * In the EXEC_BACKEND case, the limit here must match the size of the
- * ShmemBackendArray, since all these processes will have cancel codes.
+ * The limit here must match the sizes of the per-child-process arrays;
+ * see comments for MaxLivePostmasterChildren().
*/
- if (CountChildren() >= 2 * MaxBackends)
+ if (CountChildren() >= MaxLivePostmasterChildren())
return CAC_TOOMANY;
return CAC_OK;
/*
* 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
- * assume everything is all right and simply remove the backend from the
- * active backend list.
+ * assume everything is all right and proceed to remove the backend from
+ * the active backend list.
*/
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
{
if (bp->pid == pid)
{
-#ifdef EXEC_BACKEND
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
+ }
DLRemove(curr);
free(bp);
break;
/*
* Found entry for freshly-dead backend, so remove it.
*/
-#ifdef EXEC_BACKEND
if (!bp->dead_end)
- ShmemBackendArrayRemove(pid);
+ {
+ (void) ReleasePostmasterChildSlot(bp->child_slot);
+#ifdef EXEC_BACKEND
+ ShmemBackendArrayRemove(bp);
#endif
+ }
DLRemove(curr);
free(bp);
/* Keep looping so we can signal remaining backends */
pid_t pid;
/*
- * 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();
-
- /*
- * Make room for backend data structure. Better before the fork() so we
+ * Create backend data structure. Better before the fork() so we
* can handle failure cleanly.
*/
bn = (Backend *) malloc(sizeof(Backend));
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 */
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
pid = backend_forkexec(port);
* of backends.
*/
bn->pid = pid;
- bn->cancel_key = MyCancelKey;
bn->is_autovacuum = false;
- bn->dead_end = (port->canAcceptConnections != CAC_OK &&
- port->canAcceptConnections != CAC_WAITBACKUP);
DLInitElem(&bn->elem, bn);
DLAddHead(BackendList, &bn->elem);
#ifdef EXEC_BACKEND
*/
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));
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();
if (bn->pid > 0)
{
- bn->cancel_key = MyCancelKey;
bn->is_autovacuum = true;
- bn->dead_end = false;
DLInitElem(&bn->elem, bn);
DLAddHead(BackendList, &bn->elem);
#ifdef EXEC_BACKEND
}
+/*
+ * 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
/*
extern slock_t *ProcStructLock;
extern PROC_HDR *ProcGlobal;
extern PGPROC *AuxiliaryProcs;
+extern PMSignalData *PMSignalState;
extern int pgStatSock;
#ifndef WIN32
memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
param->MyCancelKey = MyCancelKey;
+ param->MyPMChildSlot = MyPMChildSlot;
param->UsedShmemSegID = UsedShmemSegID;
param->UsedShmemSegAddr = UsedShmemSegAddr;
param->ProcStructLock = ProcStructLock;
param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs;
+ param->PMSignalState = PMSignalState;
write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid);
param->PostmasterPid = PostmasterPid;
memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
MyCancelKey = param->MyCancelKey;
+ MyPMChildSlot = param->MyPMChildSlot;
UsedShmemSegID = param->UsedShmemSegID;
UsedShmemSegAddr = param->UsedShmemSegAddr;
ProcStructLock = param->ProcStructLock;
ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs;
+ PMSignalState = param->PMSignalState;
read_inheritable_socket(&pgStatSock, ¶m->pgStatSock);
PostmasterPid = param->PostmasterPid;
Size
ShmemBackendArraySize(void)
{
- return mul_size(NUM_BACKENDARRAY_ELEMS, sizeof(Backend));
+ return mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
}
void
static void
ShmemBackendArrayAdd(Backend *bn)
{
- int i;
-
- /* Find an empty slot */
- for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
- {
- if (ShmemBackendArray[i].pid == 0)
- {
- ShmemBackendArray[i] = *bn;
- return;
- }
- }
+ /* The array slot corresponding to my PMChildSlot should be free */
+ int i = bn->child_slot - 1;
- ereport(FATAL,
- (errmsg_internal("no free slots in shmem backend array")));
+ Assert(ShmemBackendArray[i].pid == 0);
+ ShmemBackendArray[i] = *bn;
}
static void
-ShmemBackendArrayRemove(pid_t pid)
+ShmemBackendArrayRemove(Backend *bn)
{
- int i;
-
- for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
- {
- if (ShmemBackendArray[i].pid == pid)
- {
- /* Mark the slot as empty */
- ShmemBackendArray[i].pid = 0;
- return;
- }
- }
+ int i = bn->child_slot - 1;
- ereport(WARNING,
- (errmsg_internal("could not find backend entry with pid %d",
- (int) pid)));
+ Assert(ShmemBackendArray[i].pid == bn->pid);
+ /* Mark the slot as empty */
+ ShmemBackendArray[i].pid = 0;
}
+
#endif /* EXEC_BACKEND */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
size = add_size(size, ProcArrayShmemSize());
size = add_size(size, BackendStatusShmemSize());
size = add_size(size, SInvalShmemSize());
+ size = add_size(size, PMSignalShmemSize());
size = add_size(size, BgWriterShmemSize());
size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, BTreeShmemSize());
/*
* Set up interprocess signaling mechanisms
*/
- PMSignalInit();
+ PMSignalShmemInit();
BgWriterShmemInit();
AutoVacuumShmemInit();
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* The flags are actually declared as "volatile sig_atomic_t" for maximum
* portability. This should ensure that loads and stores of the flag
* 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
-PMSignalInit(void)
+PMSignalShmemInit(void)
{
bool found;
- PMSignalFlags = (sig_atomic_t *)
- ShmemInitStruct("PMSignalFlags",
- NUM_PMSIGNALS * sizeof(sig_atomic_t),
- &found);
+ PMSignalState = (PMSignalData *)
+ ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
if (!found)
- MemSet(PMSignalFlags, 0, NUM_PMSIGNALS * sizeof(sig_atomic_t));
+ {
+ MemSet(PMSignalState, 0, PMSignalShmemSize());
+ PMSignalState->num_child_flags = MaxLivePostmasterChildren();
+ }
}
/*
if (!IsUnderPostmaster)
return;
/* Atomically set the proper flag */
- PMSignalFlags[reason] = true;
+ PMSignalState->PMSignalFlags[reason] = true;
/* Send signal to postmaster */
kill(PostmasterPid, SIGUSR1);
}
CheckPostmasterSignal(PMSignalReason reason)
{
/* 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 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
*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "postmaster/autovacuum.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
+#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/spin.h"
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
* prepared for us by InitProcGlobal.
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 */
if (AutovacuumLauncherPid != 0)
kill(AutovacuumLauncherPid, SIGUSR1);
*
*
* 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
* Globals used all over the place should be declared here and not
pg_time_t MyStartTime;
struct Port *MyProcPort;
long MyCancelKey;
+int MyPMChildSlot;
/*
* DataDir is the absolute path to the top level of the PGDATA directory tree.
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* 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
* some of the information in this file should be moved to other files.
extern PGDLLIMPORT pg_time_t MyStartTime;
extern PGDLLIMPORT struct Port *MyProcPort;
extern long MyCancelKey;
+extern int MyPMChildSlot;
extern char OutputFileName[];
extern PGDLLIMPORT char my_exec_path[];
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* 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 $
*
*-------------------------------------------------------------------------
*/
extern int PostmasterMain(int argc, char *argv[]);
extern void ClosePostmasterPorts(bool am_syslogger);
+extern int MaxLivePostmasterChildren(void);
+
#ifdef EXEC_BACKEND
extern pid_t postmaster_forkexec(int argc, char *argv[]);
extern int SubPostmasterMain(int argc, char *argv[]);
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* 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 $
*
*-------------------------------------------------------------------------
*/
NUM_PMSIGNALS /* Must be last value of enum! */
} PMSignalReason;
+/* PMSignalData is an opaque struct, details known only within pmsignal.c */
+typedef struct PMSignalData PMSignalData;
+
/*
* prototypes for functions in pmsignal.c
*/
-extern void PMSignalInit(void);
+extern Size PMSignalShmemSize(void);
+extern void PMSignalShmemInit(void);
extern void SendPostmasterSignal(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);
#endif /* PMSIGNAL_H */