]> granicus.if.org Git - postgresql/commitdiff
Install a "dead man switch" to allow the postmaster to detect cases where
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 May 2009 19:59:00 +0000 (19:59 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 May 2009 19:59:00 +0000 (19:59 +0000)
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.

src/backend/postmaster/postmaster.c
src/backend/storage/ipc/ipci.c
src/backend/storage/ipc/pmsignal.c
src/backend/storage/lmgr/proc.c
src/backend/utils/init/globals.c
src/include/miscadmin.h
src/include/postmaster/postmaster.h
src/include/storage/pmsignal.h

index 7a20fadcd8ae4e87f89d84a78c1861d8aca0e23c..fbeb342a907e651b599bcf90d48b3ee460dccad7 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * 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 */
@@ -149,15 +151,6 @@ typedef struct bkend
 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
 
@@ -404,6 +397,7 @@ typedef struct
        char            DataDir[MAXPGPATH];
        int                     ListenSocket[MAXLISTEN];
        long            MyCancelKey;
+       int                     MyPMChildSlot;
        unsigned long UsedShmemSegID;
        void       *UsedShmemSegAddr;
        slock_t    *ShmemLock;
@@ -413,6 +407,7 @@ typedef struct
        slock_t    *ProcStructLock;
        PROC_HDR   *ProcGlobal;
        PGPROC     *AuxiliaryProcs;
+       PMSignalData *PMSignalState;
        InheritableSocket pgStatSock;
        pid_t           PostmasterPid;
        TimestampTz PgStartTime;
@@ -443,7 +438,7 @@ static bool save_backend_variables(BackendParameters * param, Port *port,
 #endif
 
 static void ShmemBackendArrayAdd(Backend *bn);
-static void ShmemBackendArrayRemove(pid_t pid);
+static void ShmemBackendArrayRemove(Backend *bn);
 #endif   /* EXEC_BACKEND */
 
 #define StartupDataBase()              StartChildProcess(StartupProcess)
@@ -1771,7 +1766,7 @@ processCancelRequest(Port *port, void *pkt)
        {
                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
@@ -1836,10 +1831,10 @@ canAcceptConnections(void)
         * 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;
@@ -2439,8 +2434,8 @@ CleanupBackend(int pid,
        /*
         * 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))
        {
@@ -2454,10 +2449,21 @@ CleanupBackend(int pid,
 
                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;
@@ -2500,10 +2506,13 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
                        /*
                         * 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 */
@@ -2931,14 +2940,7 @@ BackendStartup(Port *port)
        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));
@@ -2950,8 +2952,26 @@ BackendStartup(Port *port)
                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);
@@ -3009,10 +3029,7 @@ BackendStartup(Port *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
@@ -4271,23 +4288,26 @@ StartAutovacuumWorker(void)
         */
        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
@@ -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
 
 /*
@@ -4364,6 +4402,7 @@ extern LWLock *LWLockArray;
 extern slock_t *ProcStructLock;
 extern PROC_HDR *ProcGlobal;
 extern PGPROC *AuxiliaryProcs;
+extern PMSignalData *PMSignalState;
 extern int     pgStatSock;
 
 #ifndef WIN32
@@ -4395,6 +4434,7 @@ save_backend_variables(BackendParameters * param, Port *port,
        memcpy(&param->ListenSocket, &ListenSocket, sizeof(ListenSocket));
 
        param->MyCancelKey = MyCancelKey;
+       param->MyPMChildSlot = MyPMChildSlot;
 
        param->UsedShmemSegID = UsedShmemSegID;
        param->UsedShmemSegAddr = UsedShmemSegAddr;
@@ -4407,6 +4447,7 @@ save_backend_variables(BackendParameters * param, Port *port,
        param->ProcStructLock = ProcStructLock;
        param->ProcGlobal = ProcGlobal;
        param->AuxiliaryProcs = AuxiliaryProcs;
+       param->PMSignalState = PMSignalState;
        write_inheritable_socket(&param->pgStatSock, pgStatSock, childPid);
 
        param->PostmasterPid = PostmasterPid;
@@ -4601,6 +4642,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
        memcpy(&ListenSocket, &param->ListenSocket, sizeof(ListenSocket));
 
        MyCancelKey = param->MyCancelKey;
+       MyPMChildSlot = param->MyPMChildSlot;
 
        UsedShmemSegID = param->UsedShmemSegID;
        UsedShmemSegAddr = param->UsedShmemSegAddr;
@@ -4613,6 +4655,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
        ProcStructLock = param->ProcStructLock;
        ProcGlobal = param->ProcGlobal;
        AuxiliaryProcs = param->AuxiliaryProcs;
+       PMSignalState = param->PMSignalState;
        read_inheritable_socket(&pgStatSock, &param->pgStatSock);
 
        PostmasterPid = param->PostmasterPid;
@@ -4642,7 +4685,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
 Size
 ShmemBackendArraySize(void)
 {
-       return mul_size(NUM_BACKENDARRAY_ELEMS, sizeof(Backend));
+       return mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
 }
 
 void
@@ -4658,41 +4701,23 @@ ShmemBackendArrayAllocation(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 */
 
 
index f85f0a66df208e9e04b269fdb1a7883a523e8a17..281215d2189addc2536a429fbbc09321397a7805 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
                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());
@@ -206,7 +207,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
        /*
         * Set up interprocess signaling mechanisms
         */
-       PMSignalInit();
+       PMSignalShmemInit();
        BgWriterShmemInit();
        AutoVacuumShmemInit();
 
index 02356de91b01a761631f142f5578f7149fc5b5ad..c77831dd25cc2f98b5525dfbeb757b6d6953828c 100644 (file)
@@ -8,7 +8,7 @@
  * 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();
+       }
 }
 
 /*
@@ -66,7 +109,7 @@ SendPostmasterSignal(PMSignalReason reason)
        if (!IsUnderPostmaster)
                return;
        /* Atomically set the proper flag */
-       PMSignalFlags[reason] = true;
+       PMSignalState->PMSignalFlags[reason] = true;
        /* Send signal to postmaster */
        kill(PostmasterPid, SIGUSR1);
 }
@@ -80,14 +123,105 @@ bool
 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
  *
index e108179e0bad159af75e8ec46c62401f0ff7acb6..8f5b4e330471240e471404a234caba7a48c52d0a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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 @@
 #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"
@@ -274,6 +275,14 @@ InitProcess(void)
                                 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.
@@ -614,6 +623,13 @@ ProcKill(int code, Datum arg)
 
        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);
index a7d776b2cc9202649312c2480446edef46c9378e..6073e0709ac11e3ace7128dd47d82f8b0a275102 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
@@ -36,6 +36,7 @@ int                   MyProcPid;
 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.
index 57ee0f93dd4c54b40348a2687540de6dc263cfcb..51649380d51c118246af490a7c4cadeb4cb67823 100644 (file)
@@ -13,7 +13,7 @@
  * 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.
@@ -137,6 +137,7 @@ extern PGDLLIMPORT int MyProcPid;
 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[];
index 3abffa8b1c72cc763831872ec8ef5986a09ef0c1..3b8061e4f7dc0d05a985a4185d5c1b3f351e3c9b 100644 (file)
@@ -6,7 +6,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,8 @@ extern const char *progname;
 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[]);
index bd6cff154ef80002ad135325dab60105d157d258..ebfc1b8aa65099db5bdcaf7444893dc0a0732380 100644 (file)
@@ -7,7 +7,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,12 +33,20 @@ typedef enum
        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 */