Create a multiplexing structure for signals to Postgres child processes.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 31 Jul 2009 20:26:23 +0000 (20:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 31 Jul 2009 20:26:23 +0000 (20:26 +0000)
This patch gets us out from under the Unix limitation of two user-defined
signal types.  We already had done something similar for signals directed to
the postmaster process; this adds multiplexing for signals directed to
backends and auxiliary processes (so long as they're connected to shared
memory).

As proof of concept, replace the former usage of SIGUSR1 and SIGUSR2
for backends with use of the multiplexing mechanism.  There are still some
hard-wired definitions of SIGUSR1 and SIGUSR2 for other process types,
but getting rid of those doesn't seem interesting at the moment.

Fujii Masao

16 files changed:
src/backend/bootstrap/bootstrap.c
src/backend/commands/async.c
src/backend/postmaster/autovacuum.c
src/backend/postmaster/bgwriter.c
src/backend/postmaster/walwriter.c
src/backend/storage/ipc/Makefile
src/backend/storage/ipc/ipci.c
src/backend/storage/ipc/procsignal.c [new file with mode: 0644]
src/backend/storage/ipc/sinval.c
src/backend/storage/ipc/sinvaladt.c
src/backend/tcop/postgres.c
src/backend/utils/init/postinit.c
src/include/bootstrap/bootstrap.h
src/include/commands/async.h
src/include/storage/procsignal.h [new file with mode: 0644]
src/include/storage/sinval.h

index 13d5bcb43615d5f04551e2c77bb2d26f6c56718a..497d0669475b36c74a764c1cfeb2fc6b19694856 100644 (file)
@@ -35,6 +35,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
+#include "storage/procsignal.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -388,6 +389,19 @@ AuxiliaryProcessMain(int argc, char *argv[])
                InitAuxiliaryProcess();
 #endif
 
+               /*
+                * Assign the ProcSignalSlot for an auxiliary process.  Since it
+                * doesn't have a BackendId, the slot is statically allocated based on
+                * the auxiliary process type (auxType).  Backends use slots indexed
+                * in the range from 1 to MaxBackends (inclusive), so we use
+                * MaxBackends + AuxProcType + 1 as the index of the slot for an
+                * auxiliary process.
+                *
+                * This will need rethinking if we ever want more than one of a
+                * particular auxiliary process type.
+                */
+               ProcSignalInit(MaxBackends + auxType + 1);
+
                /* finish setting up bufmgr.c */
                InitBufferPoolBackend();
 
index 89c2bc58ee4c9838de84f21eea525e5426d333f1..83f941b377b1eed6e9f63fc587e8cbc88a797a3e 100644 (file)
  *       If the listenerPID in a matching tuple is ours, we just send a notify
  *       message to our own front end.  If it is not ours, and "notification"
  *       is not already nonzero, we set notification to our own PID and send a
- *       SIGUSR2 signal to the receiving process (indicated by listenerPID).
+ *       PROCSIG_NOTIFY_INTERRUPT signal to the receiving process (indicated by
+ *       listenerPID).
  *       BTW: if the signal operation fails, we presume that the listener backend
  *       crashed without removing this tuple, and remove the tuple for it.
  *
- * 4. Upon receipt of a SIGUSR2 signal, the signal handler can call inbound-
- *       notify processing immediately if this backend is idle (ie, it is
- *       waiting for a frontend command and is not within a transaction block).
- *       Otherwise the handler may only set a flag, which will cause the
+ * 4. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler
+ *       can call inbound-notify processing immediately if this backend is idle
+ *       (ie, it is waiting for a frontend command and is not within a transaction
+ *       block).  Otherwise the handler may only set a flag, which will cause the
  *       processing to occur just before we next go idle.
  *
  * 5. Inbound-notify processing consists of scanning pg_listener for tuples
@@ -95,6 +96,7 @@
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
+#include "storage/procsignal.h"
 #include "storage/sinval.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
@@ -634,12 +636,17 @@ Send_Notify(Relation lRel)
 
                        /*
                         * If someone has already notified this listener, we don't bother
-                        * modifying the table, but we do still send a SIGUSR2 signal,
-                        * just in case that backend missed the earlier signal for some
-                        * reason.      It's OK to send the signal first, because the other
-                        * guy can't read pg_listener until we unlock it.
+                        * modifying the table, but we do still send a NOTIFY_INTERRUPT
+                        * signal, just in case that backend missed the earlier signal for
+                        * some reason.  It's OK to send the signal first, because the
+                        * other guy can't read pg_listener until we unlock it.
+                        *
+                        * Note: we don't have the other guy's BackendId available, so
+                        * this will incur a search of the ProcSignal table.  That's
+                        * probably not worth worrying about.
                         */
-                       if (kill(listenerPID, SIGUSR2) < 0)
+                       if (SendProcSignal(listenerPID, PROCSIG_NOTIFY_INTERRUPT,
+                                                          InvalidBackendId) < 0)
                        {
                                /*
                                 * Get rid of pg_listener entry if it refers to a PID that no
@@ -777,24 +784,22 @@ AtSubAbort_Notify(void)
 }
 
 /*
- * NotifyInterruptHandler
+ * HandleNotifyInterrupt
  *
- *             This is the signal handler for SIGUSR2.
+ *             This is called when PROCSIG_NOTIFY_INTERRUPT is received.
  *
  *             If we are idle (notifyInterruptEnabled is set), we can safely invoke
  *             ProcessIncomingNotify directly.  Otherwise, just set a flag
  *             to do it later.
  */
 void
-NotifyInterruptHandler(SIGNAL_ARGS)
+HandleNotifyInterrupt(void)
 {
-       int                     save_errno = errno;
-
        /*
-        * Note: this is a SIGNAL HANDLER.      You must be very wary what you do
-        * here. Some helpful soul had this routine sprinkled with TPRINTFs, which
-        * would likely lead to corruption of stdio buffers if they were ever
-        * turned on.
+        * Note: this is called by a SIGNAL HANDLER. You must be very wary what
+        * you do here. Some helpful soul had this routine sprinkled with
+        * TPRINTFs, which would likely lead to corruption of stdio buffers if
+        * they were ever turned on.
         */
 
        /* Don't joggle the elbow of proc_exit */
@@ -815,7 +820,7 @@ NotifyInterruptHandler(SIGNAL_ARGS)
 
                /*
                 * I'm not sure whether some flavors of Unix might allow another
-                * SIGUSR2 occurrence to recursively interrupt this routine. To cope
+                * SIGUSR1 occurrence to recursively interrupt this routine. To cope
                 * with the possibility, we do the same sort of dance that
                 * EnableNotifyInterrupt must do --- see that routine for comments.
                 */
@@ -831,12 +836,12 @@ NotifyInterruptHandler(SIGNAL_ARGS)
                        {
                                /* Here, it is finally safe to do stuff. */
                                if (Trace_notify)
-                                       elog(DEBUG1, "NotifyInterruptHandler: perform async notify");
+                                       elog(DEBUG1, "HandleNotifyInterrupt: perform async notify");
 
                                ProcessIncomingNotify();
 
                                if (Trace_notify)
-                                       elog(DEBUG1, "NotifyInterruptHandler: done");
+                                       elog(DEBUG1, "HandleNotifyInterrupt: done");
                        }
                }
 
@@ -854,8 +859,6 @@ NotifyInterruptHandler(SIGNAL_ARGS)
                 */
                notifyInterruptOccurred = 1;
        }
-
-       errno = save_errno;
 }
 
 /*
@@ -922,8 +925,8 @@ EnableNotifyInterrupt(void)
  *             a frontend command.  Signal handler execution of inbound notifies
  *             is disabled until the next EnableNotifyInterrupt call.
  *
- *             The SIGUSR1 signal handler also needs to call this, so as to
- *             prevent conflicts if one signal interrupts the other.  So we
+ *             The PROCSIG_CATCHUP_INTERRUPT signal handler also needs to call this,
+ *             so as to prevent conflicts if one signal interrupts the other.  So we
  *             must return the previous state of the flag.
  */
 bool
@@ -940,8 +943,8 @@ DisableNotifyInterrupt(void)
  * ProcessIncomingNotify
  *
  *             Deal with arriving NOTIFYs from other backends.
- *             This is called either directly from the SIGUSR2 signal handler,
- *             or the next time control reaches the outer idle loop.
+ *             This is called either directly from the PROCSIG_NOTIFY_INTERRUPT
+ *             signal handler, or the next time control reaches the outer idle loop.
  *             Scan pg_listener for arriving notifies, report them to my front end,
  *             and clear the notification field in pg_listener until next time.
  *
@@ -961,7 +964,7 @@ ProcessIncomingNotify(void)
                                nulls[Natts_pg_listener];
        bool            catchup_enabled;
 
-       /* Must prevent SIGUSR1 interrupt while I am running */
+       /* Must prevent catchup interrupt while I am running */
        catchup_enabled = DisableCatchupInterrupt();
 
        if (Trace_notify)
index 9be5b661cccdea01b0be8a1eb47f6e7a461c435a..db2585d3db4f288aa2932113ee12b6763007585d 100644 (file)
@@ -91,6 +91,7 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "tcop/tcopprot.h"
 #include "utils/dynahash.h"
@@ -436,7 +437,6 @@ AutoVacLauncherMain(int argc, char *argv[])
 
        pqsignal(SIGPIPE, SIG_IGN);
        pqsignal(SIGUSR1, avl_sigusr1_handler);
-       /* We don't listen for async notifies */
        pqsignal(SIGUSR2, SIG_IGN);
        pqsignal(SIGFPE, FloatExceptionHandler);
        pqsignal(SIGCHLD, SIG_DFL);
@@ -1322,7 +1322,7 @@ avl_sighup_handler(SIGNAL_ARGS)
        got_SIGHUP = true;
 }
 
-/* SIGUSR1: a worker is up and running, or just finished */
+/* SIGUSR1: a worker is up and running, or just finished, or failed to fork */
 static void
 avl_sigusr1_handler(SIGNAL_ARGS)
 {
@@ -1501,8 +1501,7 @@ AutoVacWorkerMain(int argc, char *argv[])
        pqsignal(SIGALRM, handle_sig_alarm);
 
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, CatchupInterruptHandler);
-       /* We don't listen for async notifies */
+       pqsignal(SIGUSR1, procsignal_sigusr1_handler);
        pqsignal(SIGUSR2, SIG_IGN);
        pqsignal(SIGFPE, FloatExceptionHandler);
        pqsignal(SIGCHLD, SIG_DFL);
index 39656c12a8230ddcfbc34b0cd13593e21f2cad69..a2c32a7e0c758efacc0eece57f2198aa039324e4 100644 (file)
@@ -223,7 +223,7 @@ BackgroundWriterMain(void)
         * tell us it's okay to shut down (via SIGUSR2).
         *
         * SIGUSR1 is presently unused; keep it spare in case someday we want this
-        * process to participate in sinval messaging.
+        * process to participate in ProcSignal signalling.
         */
        pqsignal(SIGHUP, BgSigHupHandler);      /* set flag to read config file */
        pqsignal(SIGINT, ReqCheckpointHandler);         /* request checkpoint */
@@ -231,7 +231,7 @@ BackgroundWriterMain(void)
        pqsignal(SIGQUIT, bg_quickdie);         /* hard crash time */
        pqsignal(SIGALRM, SIG_IGN);
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, SIG_IGN); /* reserve for sinval */
+       pqsignal(SIGUSR1, SIG_IGN); /* reserve for ProcSignal */
        pqsignal(SIGUSR2, ReqShutdownHandler);          /* request shutdown */
 
        /*
index 186010ddb890f1b95d2528cc219239f59afb9e45..90b8cfe0d50f8841b9f9f76b5a1941f0a4c8a2a5 100644 (file)
@@ -113,7 +113,7 @@ WalWriterMain(void)
        pqsignal(SIGQUIT, wal_quickdie);        /* hard crash time */
        pqsignal(SIGALRM, SIG_IGN);
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, SIG_IGN); /* reserve for sinval */
+       pqsignal(SIGUSR1, SIG_IGN); /* reserve for ProcSignal */
        pqsignal(SIGUSR2, SIG_IGN); /* not used */
 
        /*
index 4d390bd6689e951bb1cd0f1aeb2cc6c212c966ee..3433967fd1c7c78f6fdd7d3c31d5533caf217dab 100644 (file)
@@ -15,7 +15,7 @@ override CFLAGS+= -fno-inline
 endif
 endif
 
-OBJS = ipc.o ipci.o pmsignal.o procarray.o shmem.o shmqueue.o \
+OBJS = ipc.o ipci.o pmsignal.o procarray.o procsignal.o shmem.o shmqueue.o \
        sinval.o sinvaladt.o
 
 include $(top_srcdir)/src/backend/common.mk
index 30228675cacc116c05ea8a2fb8fdd999bacfbc65..3ef55157c859ef77fec88dd9bb50bf9dcbcd8010 100644 (file)
@@ -30,6 +30,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/pmsignal.h"
 #include "storage/procarray.h"
+#include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
 
@@ -112,6 +113,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
                size = add_size(size, BackendStatusShmemSize());
                size = add_size(size, SInvalShmemSize());
                size = add_size(size, PMSignalShmemSize());
+               size = add_size(size, ProcSignalShmemSize());
                size = add_size(size, BgWriterShmemSize());
                size = add_size(size, AutoVacuumShmemSize());
                size = add_size(size, BTreeShmemSize());
@@ -208,6 +210,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
         * Set up interprocess signaling mechanisms
         */
        PMSignalShmemInit();
+       ProcSignalShmemInit();
        BgWriterShmemInit();
        AutoVacuumShmemInit();
 
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
new file mode 100644 (file)
index 0000000..480af00
--- /dev/null
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * procsignal.c
+ *       Routines for interprocess signalling
+ *
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+#include "bootstrap/bootstrap.h"
+#include "commands/async.h"
+#include "miscadmin.h"
+#include "storage/ipc.h"
+#include "storage/procsignal.h"
+#include "storage/shmem.h"
+#include "storage/sinval.h"
+
+
+/*
+ * The SIGUSR1 signal is multiplexed to support signalling multiple event
+ * types. The specific reason is communicated via flags in shared memory.
+ * We keep a boolean flag for each possible "reason", so that different
+ * reasons can be signaled to a process concurrently.  (However, if the same
+ * reason is signaled more than once nearly simultaneously, the process may
+ * observe it only once.)
+ *
+ * Each process that wants to receive signals registers its process ID 
+ * in the ProcSignalSlots array. The array is indexed by backend ID to make
+ * slot allocation simple, and to avoid having to search the array when you
+ * know the backend ID of the process you're signalling.  (We do support
+ * signalling without backend ID, but it's a bit less efficient.)
+ * 
+ * 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.
+ */
+typedef struct
+{
+       pid_t           pss_pid;
+       sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
+} ProcSignalSlot;
+
+/*
+ * We reserve a slot for each possible BackendId, plus one for each
+ * possible auxiliary process type.  (This scheme assumes there is not
+ * more than one of any auxiliary process type at a time.)
+ */
+#define NumProcSignalSlots  (MaxBackends + NUM_AUXPROCTYPES)
+
+static ProcSignalSlot *ProcSignalSlots = NULL;
+static volatile ProcSignalSlot *MyProcSignalSlot = NULL;
+
+static bool CheckProcSignal(ProcSignalReason reason);
+static void CleanupProcSignalState(int status, Datum arg);
+
+/*
+ * ProcSignalShmemInit
+ *             Compute space needed for procsignal's shared memory
+ */
+Size
+ProcSignalShmemSize(void)
+{
+       return NumProcSignalSlots * sizeof(ProcSignalSlot);
+}
+
+/*
+ * ProcSignalShmemInit
+ *             Allocate and initialize procsignal's shared memory
+ */
+void
+ProcSignalShmemInit(void)
+{
+       Size            size = ProcSignalShmemSize();
+       bool            found;
+
+       ProcSignalSlots = (ProcSignalSlot *)
+               ShmemInitStruct("ProcSignalSlots", size, &found);
+
+       /* If we're first, set everything to zeroes */
+       if (!found)
+               MemSet(ProcSignalSlots, 0, size);
+}
+
+/*
+ * ProcSignalInit
+ *             Register the current process in the procsignal array
+ *
+ * The passed index should be my BackendId if the process has one,
+ * or MaxBackends + aux process type if not.
+ */
+void
+ProcSignalInit(int pss_idx)
+{
+       volatile ProcSignalSlot *slot;
+
+       Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots);
+
+       slot = &ProcSignalSlots[pss_idx - 1];
+
+       /* sanity check */
+       if (slot->pss_pid != 0)
+               elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
+                        MyProcPid, pss_idx);
+
+       /* Clear out any leftover signal reasons */
+       MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
+
+       /* Mark slot with my PID */
+       slot->pss_pid = MyProcPid;
+
+       /* Remember slot location for CheckProcSignal */
+       MyProcSignalSlot = slot;
+
+       /* Set up to release the slot on process exit */
+       on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx));
+}
+
+/*
+ * CleanupProcSignalState
+ *             Remove current process from ProcSignalSlots
+ *
+ * This function is called via on_shmem_exit() during backend shutdown.
+ */
+static void
+CleanupProcSignalState(int status, Datum arg)
+{
+       int                     pss_idx = DatumGetInt32(arg);
+       volatile ProcSignalSlot *slot;
+
+       slot = &ProcSignalSlots[pss_idx - 1];
+       Assert(slot == MyProcSignalSlot);
+
+       /* sanity check */
+       if (slot->pss_pid != MyProcPid)
+       {
+               /*
+                * don't ERROR here. We're exiting anyway, and don't want to
+                * get into infinite loop trying to exit
+                */
+               elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d",
+                        MyProcPid, pss_idx, (int) slot->pss_pid);
+               return;                                 /* XXX better to zero the slot anyway? */
+       }
+
+       slot->pss_pid = 0;
+}
+
+/*
+ * SendProcSignal
+ *             Send a signal to a Postgres process
+ *
+ * Providing backendId is optional, but it will speed up the operation.
+ *
+ * On success (a signal was sent), zero is returned.
+ * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).
+ *
+ * Not to be confused with ProcSendSignal
+ */
+int
+SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
+{
+       volatile ProcSignalSlot *slot;
+
+       if (backendId != InvalidBackendId)
+       {
+               slot = &ProcSignalSlots[backendId - 1];
+
+               /*
+                * Note: Since there's no locking, it's possible that the target
+                * process detaches from shared memory and exits right after this
+                * test, before we set the flag and send signal. And the signal slot
+                * might even be recycled by a new process, so it's remotely possible
+                * that we set a flag for a wrong process. That's OK, all the signals
+                * are such that no harm is done if they're mistakenly fired.
+                */
+               if (slot->pss_pid == pid)
+               {
+                       /* Atomically set the proper flag */
+                       slot->pss_signalFlags[reason] = true;
+                       /* Send signal */
+                       return kill(pid, SIGUSR1);
+               }
+       }
+       else
+       {
+               /*
+                * BackendId not provided, so search the array using pid.  We search
+                * the array back to front so as to reduce search overhead.  Passing
+                * InvalidBackendId means that the target is most likely an auxiliary
+                * process, which will have a slot near the end of the array.
+                */
+               int             i;
+
+               for (i = NumProcSignalSlots - 1; i >= 0; i--)
+               {
+                       slot = &ProcSignalSlots[i];
+
+                       if (slot->pss_pid == pid)
+                       {
+                               /* the above note about race conditions applies here too */
+
+                               /* Atomically set the proper flag */
+                               slot->pss_signalFlags[reason] = true;
+                               /* Send signal */
+                               return kill(pid, SIGUSR1);
+                       }
+               }
+       }
+
+       errno = ESRCH;
+       return -1;
+}
+
+/*
+ * CheckProcSignal - check to see if a particular reason has been
+ * signaled, and clear the signal flag.  Should be called after receiving
+ * SIGUSR1.
+ */
+static bool
+CheckProcSignal(ProcSignalReason reason)
+{
+       volatile ProcSignalSlot *slot = MyProcSignalSlot;
+
+       if (slot != NULL)
+       {
+               /* Careful here --- don't clear flag if we haven't seen it set */
+               if (slot->pss_signalFlags[reason])
+               {
+                       slot->pss_signalFlags[reason] = false;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/*
+ * procsignal_sigusr1_handler - handle SIGUSR1 signal.
+ */
+void
+procsignal_sigusr1_handler(SIGNAL_ARGS)
+{
+       int             save_errno = errno;
+
+       if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
+               HandleCatchupInterrupt();
+
+       if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
+               HandleNotifyInterrupt();
+
+       errno = save_errno;
+}
index 6ddec2ed1063688fbc94346bb60beb542940ecc6..409ea0fdec88b5b1b1cc18a7fd47f2e2e63e2fb6 100644 (file)
@@ -26,8 +26,8 @@
  * Because backends sitting idle will not be reading sinval events, we
  * need a way to give an idle backend a swift kick in the rear and make
  * it catch up before the sinval queue overflows and forces it to go
- * through a cache reset exercise.     This is done by sending SIGUSR1
- * to any backend that gets too far behind.
+ * through a cache reset exercise.     This is done by sending
+ * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind.
  *
  * State for catchup events consists of two flags: one saying whether
  * the signal handler is currently allowed to call ProcessCatchupEvent
@@ -145,9 +145,9 @@ ReceiveSharedInvalidMessages(
 
 
 /*
- * CatchupInterruptHandler
+ * HandleCatchupInterrupt
  *
- * This is the signal handler for SIGUSR1.
+ * This is called when PROCSIG_CATCHUP_INTERRUPT is received.
  *
  * If we are idle (catchupInterruptEnabled is set), we can safely
  * invoke ProcessCatchupEvent directly.  Otherwise, just set a flag
@@ -157,13 +157,11 @@ ReceiveSharedInvalidMessages(
  * since there's no longer any reason to do anything.)
  */
 void
-CatchupInterruptHandler(SIGNAL_ARGS)
+HandleCatchupInterrupt(void)
 {
-       int                     save_errno = errno;
-
        /*
-        * Note: this is a SIGNAL HANDLER.      You must be very wary what you do
-        * here.
+        * Note: this is called by a SIGNAL HANDLER. You must be very wary what
+        * you do here.
         */
 
        /* Don't joggle the elbow of proc_exit */
@@ -217,8 +215,6 @@ CatchupInterruptHandler(SIGNAL_ARGS)
                 */
                catchupInterruptOccurred = 1;
        }
-
-       errno = save_errno;
 }
 
 /*
@@ -273,8 +269,8 @@ EnableCatchupInterrupt(void)
  * a frontend command. Signal handler execution of catchup events
  * is disabled until the next EnableCatchupInterrupt call.
  *
- * The SIGUSR2 signal handler also needs to call this, so as to
- * prevent conflicts if one signal interrupts the other.  So we
+ * The PROCSIG_NOTIFY_INTERRUPT signal handler also needs to call this,
+ * so as to prevent conflicts if one signal interrupts the other.  So we
  * must return the previous state of the flag.
  */
 bool
@@ -290,18 +286,19 @@ DisableCatchupInterrupt(void)
 /*
  * ProcessCatchupEvent
  *
- * Respond to a catchup event (SIGUSR1) from another backend.
+ * Respond to a catchup event (PROCSIG_CATCHUP_INTERRUPT) from another
+ * backend.
  *
- * This is called either directly from the SIGUSR1 signal handler,
- * or the next time control reaches the outer idle loop (assuming
- * there's still anything to do by then).
+ * This is called either directly from the PROCSIG_CATCHUP_INTERRUPT
+ * signal handler, or the next time control reaches the outer idle loop
+ * (assuming there's still anything to do by then).
  */
 static void
 ProcessCatchupEvent(void)
 {
        bool            notify_enabled;
 
-       /* Must prevent SIGUSR2 interrupt while I am running */
+       /* Must prevent notify interrupt while I am running */
        notify_enabled = DisableNotifyInterrupt();
 
        /*
index 72120e96df4a2a2b37313659071eb85d0674362b..aaefa27fd9a76445212495387dc38580c029ffdd 100644 (file)
@@ -21,6 +21,7 @@
 #include "storage/backendid.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
+#include "storage/procsignal.h"
 #include "storage/shmem.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
  * we exceed CLEANUP_MIN.  Should be a power of 2 for speed.
  *
  * SIG_THRESHOLD: the minimum number of messages a backend must have fallen
- * behind before we'll send it SIGUSR1.
+ * behind before we'll send it PROCSIG_CATCHUP_INTERRUPT.
  *
  * WRITE_QUANTUM: the max number of messages to push into the buffer per
  * iteration of SIInsertDataEntries.  Noncritical but should be less than
@@ -551,7 +552,7 @@ SIGetDataEntries(SharedInvalidationMessage *data, int datasize)
  * minFree is the minimum number of message slots to make free.
  *
  * Possible side effects of this routine include marking one or more
- * backends as "reset" in the array, and sending a catchup interrupt (SIGUSR1)
+ * backends as "reset" in the array, and sending PROCSIG_CATCHUP_INTERRUPT
  * to some backend that seems to be getting too far behind.  We signal at
  * most one backend at a time, for reasons explained at the top of the file.
  *
@@ -644,18 +645,20 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
                segP->nextThreshold = (numMsgs / CLEANUP_QUANTUM + 1) * CLEANUP_QUANTUM;
 
        /*
-        * Lastly, signal anyone who needs a catchup interrupt.  Since kill()
-        * might not be fast, we don't want to hold locks while executing it.
+        * Lastly, signal anyone who needs a catchup interrupt.  Since
+        * SendProcSignal() might not be fast, we don't want to hold locks while
+        * executing it.
         */
        if (needSig)
        {
                pid_t           his_pid = needSig->procPid;
+               BackendId       his_backendId = (needSig - &segP->procState[0]) + 1;
 
                needSig->signaled = true;
                LWLockRelease(SInvalReadLock);
                LWLockRelease(SInvalWriteLock);
                elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid);
-               kill(his_pid, SIGUSR1);
+               SendProcSignal(his_pid, PROCSIG_CATCHUP_INTERRUPT, his_backendId);
                if (callerHasWriteLock)
                        LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
        }
index edd6ffe99b5c518195dce0dc7586cd41a77d0b80..2beb148d98f274f2941bef1624661009329342a1 100644 (file)
@@ -59,6 +59,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
+#include "storage/procsignal.h"
 #include "storage/sinval.h"
 #include "tcop/fastpath.h"
 #include "tcop/pquery.h"
@@ -3221,8 +3222,8 @@ PostgresMain(int argc, char *argv[], const char *username)
         * of output during who-knows-what operation...
         */
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, CatchupInterruptHandler);
-       pqsignal(SIGUSR2, NotifyInterruptHandler);
+       pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+       pqsignal(SIGUSR2, SIG_IGN);
        pqsignal(SIGFPE, FloatExceptionHandler);
 
        /*
index 327ba7c180571db72d13e56a980a04176d3f8d45..e4a5fef37c3ac401bfb87be7450bf29960a166b7 100644 (file)
@@ -39,6 +39,7 @@
 #include "storage/lmgr.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/smgr.h"
 #include "utils/acl.h"
@@ -451,6 +452,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
        if (MyBackendId > MaxBackends || MyBackendId <= 0)
                elog(FATAL, "bad backend id: %d", MyBackendId);
 
+       /* Now that we have a BackendId, we can participate in ProcSignal */
+       ProcSignalInit(MyBackendId);
+
        /*
         * bufmgr needs another initialization call too
         */
index b43408f12970f703ae3f6b3e19a5546c4ad85efa..5dd3d1dbcc317c4f82726692f7b467ff33de1823 100644 (file)
@@ -70,7 +70,9 @@ typedef enum
        BootstrapProcess,
        StartupProcess,
        BgWriterProcess,
-       WalWriterProcess
+       WalWriterProcess,
+
+       NUM_AUXPROCTYPES                        /* Must be last! */
 } AuxProcType;
 
 #endif   /* BOOTSTRAP_H */
index 448d6ba1b17d2add175e4c50ce4a956be884df0b..3dddc86d5938dc025e3b29e351f02da9561f437d 100644 (file)
@@ -29,8 +29,8 @@ extern void AtSubCommit_Notify(void);
 extern void AtSubAbort_Notify(void);
 extern void AtPrepare_Notify(void);
 
-/* signal handler for inbound notifies (SIGUSR2) */
-extern void NotifyInterruptHandler(SIGNAL_ARGS);
+/* signal handler for inbound notifies (PROCSIG_NOTIFY_INTERRUPT) */
+extern void HandleNotifyInterrupt(void);
 
 /*
  * enable/disable processing of inbound notifies directly from signal handler.
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
new file mode 100644 (file)
index 0000000..7f3b65e
--- /dev/null
@@ -0,0 +1,50 @@
+/*-------------------------------------------------------------------------
+ *
+ * procsignal.h
+ *       Routines for interprocess signalling
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCSIGNAL_H
+#define PROCSIGNAL_H
+
+#include "storage/backendid.h"
+
+
+/*
+ * Reasons for signalling a Postgres child process (a backend or an auxiliary
+ * process, like bgwriter).  We can cope with concurrent signals for different
+ * reasons.  However, if the same reason is signaled multiple times in quick
+ * succession, the process is likely to observe only one notification of it.
+ * This is okay for the present uses.
+ *
+ * Also, because of race conditions, it's important that all the signals be
+ * defined so that no harm is done if a process mistakenly receives one.
+ */
+typedef enum
+{
+       PROCSIG_CATCHUP_INTERRUPT,      /* sinval catchup interrupt */
+       PROCSIG_NOTIFY_INTERRUPT,       /* listen/notify interrupt */
+
+       NUM_PROCSIGNALS                         /* Must be last! */
+} ProcSignalReason;
+
+/*
+ * prototypes for functions in procsignal.c
+ */
+extern Size ProcSignalShmemSize(void);
+extern void ProcSignalShmemInit(void);
+
+extern void ProcSignalInit(int pss_idx);
+extern int  SendProcSignal(pid_t pid, ProcSignalReason reason,
+                                                  BackendId backendId);
+
+extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+
+#endif   /* PROCSIGNAL_H */
index 8f5c33c57c77f6b254d006cdb05964f6ed46aa39..b30805e6ae640ba233561eaf8f8293cbe4a1ec33 100644 (file)
@@ -89,8 +89,8 @@ extern void ReceiveSharedInvalidMessages(
                                          void (*invalFunction) (SharedInvalidationMessage *msg),
                                                         void (*resetFunction) (void));
 
-/* signal handler for catchup events (SIGUSR1) */
-extern void CatchupInterruptHandler(SIGNAL_ARGS);
+/* signal handler for catchup events (PROCSIG_CATCHUP_INTERRUPT) */
+extern void HandleCatchupInterrupt(void);
 
 /*
  * enable/disable processing of catchup events directly from signal handler.