]> granicus.if.org Git - postgresql/commitdiff
Provide support for multiplexing SIGUSR1 signal. The upcoming synchronous
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 9 Dec 2008 14:28:20 +0000 (14:28 +0000)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 9 Dec 2008 14:28:20 +0000 (14:28 +0000)
replication patch needs a signal, but we've already used SIGUSR1 and
SIGUSR2 in normal backends. This patch allows reusing SIGUSR1 for that,
and for other purposes too if the need arises.

src/backend/access/transam/twophase.c
src/backend/commands/async.c
src/backend/postmaster/autovacuum.c
src/backend/storage/ipc/sinval.c
src/backend/storage/ipc/sinvaladt.c
src/backend/storage/lmgr/proc.c
src/backend/tcop/postgres.c
src/include/storage/proc.h
src/include/storage/sinval.h
src/include/tcop/tcopprot.h

index 369fe97588acbe2e277d45184221555e1515e181..c500144ca9b2eb16f050c30cba79d4493e912a9d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *             $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.48 2008/11/19 10:34:50 heikki Exp $
+ *             $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.49 2008/12/09 14:28:20 heikki Exp $
  *
  * NOTES
  *             Each global transaction is associated with a global transaction
@@ -287,6 +287,7 @@ MarkAsPreparing(TransactionId xid, const char *gid,
        gxact->proc.databaseId = databaseid;
        gxact->proc.roleId = owner;
        gxact->proc.inCommit = false;
+       MemSet(gxact->proc.signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
        gxact->proc.vacuumFlags = 0;
        gxact->proc.lwWaiting = false;
        gxact->proc.lwExclusive = false;
index 195159f305359593529b6f1d56cb4830958a7af9..0767d97ef9176095fce56d04579897e971eb497f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.142 2008/11/02 01:45:27 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.143 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -915,9 +915,10 @@ 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
- *             must return the previous state of the flag.
+ *             This also needs to be called when SIGUSR1 with 
+ *             PROCSIG_CATCHUP_INTERRUPT is received, so as to prevent conflicts 
+ *             if one signal interrupts the other.  So we must return the previous 
+ *             state of the flag.
  */
 bool
 DisableNotifyInterrupt(void)
@@ -954,7 +955,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 e4ad8d95afaa42a0aa836ea1daf14d6c2fbd778f..31b5eeca5f5581d709b770f0ad8d9f76f70fe2a0 100644 (file)
@@ -55,7 +55,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.88 2008/12/04 11:42:24 heikki Exp $
+ *       $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.89 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1477,7 +1477,7 @@ AutoVacWorkerMain(int argc, char *argv[])
        pqsignal(SIGALRM, handle_sig_alarm);
 
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, CatchupInterruptHandler);
+       pqsignal(SIGUSR1, proc_sigusr1_handler);
        /* We don't listen for async notifies */
        pqsignal(SIGUSR2, SIG_IGN);
        pqsignal(SIGFPE, FloatExceptionHandler);
index e2c6ca2aec9b976105015a6ed34bbe7b09bcd1df..d002f45489ce71008dc153be524209cb3839cd5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.86 2008/06/19 21:32:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.87 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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
@@ -144,9 +144,9 @@ ReceiveSharedInvalidMessages(
 
 
 /*
- * CatchupInterruptHandler
+ * HandleCatchupInterrupt
  *
- * This is the signal handler for SIGUSR1.
+ * This is called when PROCSIG_CATCHUP_INTERRUPT signal is received.
  *
  * If we are idle (catchupInterruptEnabled is set), we can safely
  * invoke ProcessCatchupEvent directly.  Otherwise, just set a flag
@@ -156,13 +156,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 */
@@ -216,8 +214,6 @@ CatchupInterruptHandler(SIGNAL_ARGS)
                 */
                catchupInterruptOccurred = 1;
        }
-
-       errno = save_errno;
 }
 
 /*
@@ -289,7 +285,8 @@ 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
index b7933a05c422588dd5c3e9c7e7330014f2bf40d4..fe9ff43ae74bb635d0192f0ee0bfb298f103c77f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.74 2008/07/18 14:45:48 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.75 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "storage/backendid.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/shmem.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
 /* Per-backend state in shared invalidation structure */
 typedef struct ProcState
 {
-       /* procPid is zero in an inactive ProcState array entry. */
-       pid_t           procPid;                /* PID of backend, for signaling */
-       /* nextMsgNum is meaningless if procPid == 0 or resetState is true. */
+       /* proc is NULL in an inactive ProcState array entry. */
+       PGPROC     *proc;                       /* PGPROC entry of backend, for signaling */
+       /* nextMsgNum is meaningless if proc == NULL or resetState is true. */
        int                     nextMsgNum;             /* next message number to read */
        bool            resetState;             /* backend needs to reset its state */
        bool            signaled;               /* backend has been sent catchup signal */
@@ -235,7 +236,7 @@ CreateSharedInvalidationState(void)
        /* Mark all backends inactive, and initialize nextLXID */
        for (i = 0; i < shmInvalBuffer->maxBackends; i++)
        {
-               shmInvalBuffer->procState[i].procPid = 0;                       /* inactive */
+               shmInvalBuffer->procState[i].proc = NULL;                       /* inactive */
                shmInvalBuffer->procState[i].nextMsgNum = 0;            /* meaningless */
                shmInvalBuffer->procState[i].resetState = false;
                shmInvalBuffer->procState[i].signaled = false;
@@ -266,7 +267,7 @@ SharedInvalBackendInit(void)
        /* Look for a free entry in the procState array */
        for (index = 0; index < segP->lastBackend; index++)
        {
-               if (segP->procState[index].procPid == 0)                /* inactive slot? */
+               if (segP->procState[index].proc == NULL)                /* inactive slot? */
                {
                        stateP = &segP->procState[index];
                        break;
@@ -278,7 +279,7 @@ SharedInvalBackendInit(void)
                if (segP->lastBackend < segP->maxBackends)
                {
                        stateP = &segP->procState[segP->lastBackend];
-                       Assert(stateP->procPid == 0);
+                       Assert(stateP->proc == NULL);
                        segP->lastBackend++;
                }
                else
@@ -303,7 +304,7 @@ SharedInvalBackendInit(void)
        nextLocalTransactionId = stateP->nextLXID;
 
        /* mark myself active, with all extant messages already read */
-       stateP->procPid = MyProcPid;
+       stateP->proc = MyProc;
        stateP->nextMsgNum = segP->maxMsgNum;
        stateP->resetState = false;
        stateP->signaled = false;
@@ -341,7 +342,7 @@ CleanupInvalidationState(int status, Datum arg)
        stateP->nextLXID = nextLocalTransactionId;
 
        /* Mark myself inactive */
-       stateP->procPid = 0;
+       stateP->proc = NULL;
        stateP->nextMsgNum = 0;
        stateP->resetState = false;
        stateP->signaled = false;
@@ -349,7 +350,7 @@ CleanupInvalidationState(int status, Datum arg)
        /* Recompute index of last active backend */
        for (i = segP->lastBackend; i > 0; i--)
        {
-               if (segP->procState[i - 1].procPid != 0)
+               if (segP->procState[i - 1].proc != NULL)
                        break;
        }
        segP->lastBackend = i;
@@ -374,7 +375,7 @@ BackendIdIsActive(int backendID)
        {
                ProcState  *stateP = &segP->procState[backendID - 1];
 
-               result = (stateP->procPid != 0);
+               result = (stateP->proc != NULL);
        }
        else
                result = false;
@@ -590,7 +591,7 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
                int             n = stateP->nextMsgNum;
 
                /* Ignore if inactive or already in reset state */
-               if (stateP->procPid == 0 || stateP->resetState)
+               if (stateP->proc == NULL || stateP->resetState)
                        continue;
 
                /*
@@ -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;
+               PGPROC *his_proc = needSig->proc;
 
                needSig->signaled = true;
                LWLockRelease(SInvalReadLock);
                LWLockRelease(SInvalWriteLock);
-               elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid);
-               kill(his_pid, SIGUSR1);
+               elog(DEBUG4, "sending sinval catchup signal to PID %d",
+                        (int) his_proc->pid);
+               SendProcSignal(his_proc, PROCSIG_CATCHUP_INTERRUPT);
                if (callerHasWriteLock)
                        LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
        }
index 0ceff0ffe35ce337690610b290a75eb79e4d7ad1..77388f86ae07b51c5699791cdc65090d47da06bc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.202 2008/11/02 21:24:52 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.203 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -289,6 +289,7 @@ InitProcess(void)
        MyProc->databaseId = InvalidOid;
        MyProc->roleId = InvalidOid;
        MyProc->inCommit = false;
+       MemSet(MyProc->signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
        MyProc->vacuumFlags = 0;
        if (IsAutoVacuumWorkerProcess())
                MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
@@ -428,6 +429,7 @@ InitAuxiliaryProcess(void)
        MyProc->databaseId = InvalidOid;
        MyProc->roleId = InvalidOid;
        MyProc->inCommit = false;
+       MemSet(MyProc->signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
        /* we don't set the "is autovacuum" flag in the launcher */
        MyProc->vacuumFlags = 0;
        MyProc->lwWaiting = false;
@@ -1277,6 +1279,54 @@ ProcSendSignal(int pid)
                PGSemaphoreUnlock(&proc->sem);
 }
 
+/*
+ * SendProcSignal - send the signal with the reason to a process.
+ *
+ * The process can be a backend or an auxiliary process that has a PGPROC
+ * entry, like an autovacuum worker.
+ */
+void
+SendProcSignal(PGPROC *proc, ProcSignalReason reason)
+{
+       pid_t pid;
+
+       /*
+        * If the process is gone, do nothing.
+        *
+        * Since there's no locking, it's possible that the process detaches
+        * from shared memory and exits right after this test, before we set
+        * the flag and send signal. And the PGPROC entry might even be recycled
+        * by a new process, so it's remotely possible that we signal a wrong
+        * process. That's OK, all the signals are such that no harm is done.
+        */
+       pid = proc->pid;
+       if (pid == 0)
+               return;
+
+       /* Atomically set the proper flag */
+       proc->signalFlags[reason] = true;
+       /* Send SIGUSR1 to the process */
+       kill(pid, SIGUSR1);
+}
+
+/*
+ * CheckProcSignal - check to see if the particular reason has been
+ * signaled, and clear the signal flag.  Should be called after 
+ * receiving SIGUSR1.
+ */
+bool
+CheckProcSignal(ProcSignalReason reason)
+{
+       /* Careful here --- don't clear flag if we haven't seen it set */
+       if (MyProc->signalFlags[reason])
+       {
+               MyProc->signalFlags[reason] = false;
+               return true;
+       }
+
+       return false;
+}
+
 
 /*****************************************************************************
  * SIGALRM interrupt support
index f262b8014ef568e91eb5343843143fd9cd94397e..6df02a3f795fabcb0ac4c3a2a041963ae75e3ed0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.558 2008/11/30 20:51:25 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.559 2008/12/09 14:28:20 heikki Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -2436,6 +2436,23 @@ drop_unnamed_stmt(void)
  * --------------------------------
  */
 
+/*
+ * proc_sigusr1_handler - handle SIGUSR1 signal.
+ *
+ * SIGUSR1 is multiplexed to handle multiple different events. The signalFlags
+ * array in PGPROC indicates which events have been signaled.
+ */
+void
+proc_sigusr1_handler(SIGNAL_ARGS)
+{
+       int                     save_errno = errno;
+
+       if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
+               HandleCatchupInterrupt();
+
+       errno = save_errno;
+}
+
 /*
  * quickdie() occurs when signalled SIGQUIT by the postmaster.
  *
@@ -3180,7 +3197,7 @@ PostgresMain(int argc, char *argv[], const char *username)
         * of output during who-knows-what operation...
         */
        pqsignal(SIGPIPE, SIG_IGN);
-       pqsignal(SIGUSR1, CatchupInterruptHandler);
+       pqsignal(SIGUSR1, proc_sigusr1_handler);
        pqsignal(SIGUSR2, NotifyInterruptHandler);
        pqsignal(SIGFPE, FloatExceptionHandler);
 
index ccdc48884a2087253639e189a426b24e1e8490fe..3ff484ee410379f5173e43bf7553f501e89f2006 100644 (file)
@@ -7,13 +7,15 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.107 2008/11/02 21:24:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.108 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef _PROC_H_
 #define _PROC_H_
 
+#include <signal.h>
+
 #include "storage/lock.h"
 #include "storage/pg_sema.h"
 
@@ -38,6 +40,19 @@ struct XidCache
        TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
 };
 
+/*
+ * Reasons for signaling a process (a backend or an auxiliary process, like
+ * autovacuum worker). We can cope with simultaneous signals for different
+ * reasons. If the same reason is signaled multiple times in quick succession,
+ * however, the process is likely to observe only one notification of it.
+ * This is okay for the present uses.
+ */
+typedef enum
+{
+       PROCSIG_CATCHUP_INTERRUPT,      /* catchup interrupt */
+       NUM_PROCSIGNALS                         /* Must be last value of enum! */
+} ProcSignalReason;
+
 /* Flags for PGPROC->vacuumFlags */
 #define                PROC_IS_AUTOVACUUM      0x01    /* is it an autovac worker? */
 #define                PROC_IN_VACUUM          0x02    /* currently running lazy vacuum */
@@ -93,6 +108,16 @@ struct PGPROC
 
        uint8           vacuumFlags;    /* vacuum-related flags, see above */
 
+       /*
+        * SIGUSR1 signal is multiplexed for multiple purposes. signalFlags
+        * indicates which "reasons" have been signaled.
+        *
+        * The flags are 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.
+        */
+       volatile sig_atomic_t signalFlags[NUM_PROCSIGNALS];
+
        /* Info about LWLock the process is currently waiting for, if any. */
        bool            lwWaiting;              /* true if waiting for an LW lock */
        bool            lwExclusive;    /* true if waiting for exclusive access */
@@ -171,6 +196,9 @@ extern void LockWaitCancel(void);
 extern void ProcWaitForSignal(void);
 extern void ProcSendSignal(int pid);
 
+extern void SendProcSignal(PGPROC *proc, ProcSignalReason reason);
+extern bool CheckProcSignal(ProcSignalReason reason);
+
 extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
 extern bool disable_sig_alarm(bool is_statement_timeout);
 extern void handle_sig_alarm(SIGNAL_ARGS);
index 3601216f1b61052af7f9cde57bf60a7a3cdb591f..af47f8de9381bd0515fc833a95ce7a4ef2d488b3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.48 2008/06/19 21:32:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.49 2008/12/09 14:28:20 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,7 +90,7 @@ extern void ReceiveSharedInvalidMessages(
                                                         void (*resetFunction) (void));
 
 /* signal handler for catchup events (SIGUSR1) */
-extern void CatchupInterruptHandler(SIGNAL_ARGS);
+extern void HandleCatchupInterrupt(void);
 
 /*
  * enable/disable processing of catchup events directly from signal handler.
index ef87e1ee0525349be0a3536633a741fbddbc4987..3a2f4fc2e613d0d01421404dbb1f94b7c0577b6c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.93 2008/03/10 12:55:13 mha Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.94 2008/12/09 14:28:20 heikki Exp $
  *
  * OLD COMMENTS
  *       This file was created so that other c files could get the two
@@ -56,6 +56,7 @@ extern List *pg_plan_queries(List *querytrees, int cursorOptions,
 
 extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
 
+extern void proc_sigusr1_handler(SIGNAL_ARGS);
 extern void die(SIGNAL_ARGS);
 extern void quickdie(SIGNAL_ARGS);
 extern void authdie(SIGNAL_ARGS);