Header And Logo

PostgreSQL
| The world's most advanced open source database.

procsignal.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * procsignal.c
00004  *    Routines for interprocess signalling
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/storage/ipc/procsignal.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <signal.h>
00018 #include <unistd.h>
00019 
00020 #include "commands/async.h"
00021 #include "miscadmin.h"
00022 #include "storage/latch.h"
00023 #include "storage/ipc.h"
00024 #include "storage/shmem.h"
00025 #include "storage/sinval.h"
00026 #include "tcop/tcopprot.h"
00027 
00028 
00029 /*
00030  * The SIGUSR1 signal is multiplexed to support signalling multiple event
00031  * types. The specific reason is communicated via flags in shared memory.
00032  * We keep a boolean flag for each possible "reason", so that different
00033  * reasons can be signaled to a process concurrently.  (However, if the same
00034  * reason is signaled more than once nearly simultaneously, the process may
00035  * observe it only once.)
00036  *
00037  * Each process that wants to receive signals registers its process ID
00038  * in the ProcSignalSlots array. The array is indexed by backend ID to make
00039  * slot allocation simple, and to avoid having to search the array when you
00040  * know the backend ID of the process you're signalling.  (We do support
00041  * signalling without backend ID, but it's a bit less efficient.)
00042  *
00043  * The flags are actually declared as "volatile sig_atomic_t" for maximum
00044  * portability.  This should ensure that loads and stores of the flag
00045  * values are atomic, allowing us to dispense with any explicit locking.
00046  */
00047 typedef struct
00048 {
00049     pid_t       pss_pid;
00050     sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
00051 } ProcSignalSlot;
00052 
00053 /*
00054  * We reserve a slot for each possible BackendId, plus one for each
00055  * possible auxiliary process type.  (This scheme assumes there is not
00056  * more than one of any auxiliary process type at a time.)
00057  */
00058 #define NumProcSignalSlots  (MaxBackends + NUM_AUXPROCTYPES)
00059 
00060 static ProcSignalSlot *ProcSignalSlots = NULL;
00061 static volatile ProcSignalSlot *MyProcSignalSlot = NULL;
00062 
00063 static bool CheckProcSignal(ProcSignalReason reason);
00064 static void CleanupProcSignalState(int status, Datum arg);
00065 
00066 /*
00067  * ProcSignalShmemSize
00068  *      Compute space needed for procsignal's shared memory
00069  */
00070 Size
00071 ProcSignalShmemSize(void)
00072 {
00073     return NumProcSignalSlots * sizeof(ProcSignalSlot);
00074 }
00075 
00076 /*
00077  * ProcSignalShmemInit
00078  *      Allocate and initialize procsignal's shared memory
00079  */
00080 void
00081 ProcSignalShmemInit(void)
00082 {
00083     Size        size = ProcSignalShmemSize();
00084     bool        found;
00085 
00086     ProcSignalSlots = (ProcSignalSlot *)
00087         ShmemInitStruct("ProcSignalSlots", size, &found);
00088 
00089     /* If we're first, set everything to zeroes */
00090     if (!found)
00091         MemSet(ProcSignalSlots, 0, size);
00092 }
00093 
00094 /*
00095  * ProcSignalInit
00096  *      Register the current process in the procsignal array
00097  *
00098  * The passed index should be my BackendId if the process has one,
00099  * or MaxBackends + aux process type if not.
00100  */
00101 void
00102 ProcSignalInit(int pss_idx)
00103 {
00104     volatile ProcSignalSlot *slot;
00105 
00106     Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots);
00107 
00108     slot = &ProcSignalSlots[pss_idx - 1];
00109 
00110     /* sanity check */
00111     if (slot->pss_pid != 0)
00112         elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
00113              MyProcPid, pss_idx);
00114 
00115     /* Clear out any leftover signal reasons */
00116     MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
00117 
00118     /* Mark slot with my PID */
00119     slot->pss_pid = MyProcPid;
00120 
00121     /* Remember slot location for CheckProcSignal */
00122     MyProcSignalSlot = slot;
00123 
00124     /* Set up to release the slot on process exit */
00125     on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx));
00126 }
00127 
00128 /*
00129  * CleanupProcSignalState
00130  *      Remove current process from ProcSignalSlots
00131  *
00132  * This function is called via on_shmem_exit() during backend shutdown.
00133  */
00134 static void
00135 CleanupProcSignalState(int status, Datum arg)
00136 {
00137     int         pss_idx = DatumGetInt32(arg);
00138     volatile ProcSignalSlot *slot;
00139 
00140     slot = &ProcSignalSlots[pss_idx - 1];
00141     Assert(slot == MyProcSignalSlot);
00142 
00143     /* sanity check */
00144     if (slot->pss_pid != MyProcPid)
00145     {
00146         /*
00147          * don't ERROR here. We're exiting anyway, and don't want to get into
00148          * infinite loop trying to exit
00149          */
00150         elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d",
00151              MyProcPid, pss_idx, (int) slot->pss_pid);
00152         return;                 /* XXX better to zero the slot anyway? */
00153     }
00154 
00155     slot->pss_pid = 0;
00156 }
00157 
00158 /*
00159  * SendProcSignal
00160  *      Send a signal to a Postgres process
00161  *
00162  * Providing backendId is optional, but it will speed up the operation.
00163  *
00164  * On success (a signal was sent), zero is returned.
00165  * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).
00166  *
00167  * Not to be confused with ProcSendSignal
00168  */
00169 int
00170 SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
00171 {
00172     volatile ProcSignalSlot *slot;
00173 
00174     if (backendId != InvalidBackendId)
00175     {
00176         slot = &ProcSignalSlots[backendId - 1];
00177 
00178         /*
00179          * Note: Since there's no locking, it's possible that the target
00180          * process detaches from shared memory and exits right after this
00181          * test, before we set the flag and send signal. And the signal slot
00182          * might even be recycled by a new process, so it's remotely possible
00183          * that we set a flag for a wrong process. That's OK, all the signals
00184          * are such that no harm is done if they're mistakenly fired.
00185          */
00186         if (slot->pss_pid == pid)
00187         {
00188             /* Atomically set the proper flag */
00189             slot->pss_signalFlags[reason] = true;
00190             /* Send signal */
00191             return kill(pid, SIGUSR1);
00192         }
00193     }
00194     else
00195     {
00196         /*
00197          * BackendId not provided, so search the array using pid.  We search
00198          * the array back to front so as to reduce search overhead.  Passing
00199          * InvalidBackendId means that the target is most likely an auxiliary
00200          * process, which will have a slot near the end of the array.
00201          */
00202         int         i;
00203 
00204         for (i = NumProcSignalSlots - 1; i >= 0; i--)
00205         {
00206             slot = &ProcSignalSlots[i];
00207 
00208             if (slot->pss_pid == pid)
00209             {
00210                 /* the above note about race conditions applies here too */
00211 
00212                 /* Atomically set the proper flag */
00213                 slot->pss_signalFlags[reason] = true;
00214                 /* Send signal */
00215                 return kill(pid, SIGUSR1);
00216             }
00217         }
00218     }
00219 
00220     errno = ESRCH;
00221     return -1;
00222 }
00223 
00224 /*
00225  * CheckProcSignal - check to see if a particular reason has been
00226  * signaled, and clear the signal flag.  Should be called after receiving
00227  * SIGUSR1.
00228  */
00229 static bool
00230 CheckProcSignal(ProcSignalReason reason)
00231 {
00232     volatile ProcSignalSlot *slot = MyProcSignalSlot;
00233 
00234     if (slot != NULL)
00235     {
00236         /* Careful here --- don't clear flag if we haven't seen it set */
00237         if (slot->pss_signalFlags[reason])
00238         {
00239             slot->pss_signalFlags[reason] = false;
00240             return true;
00241         }
00242     }
00243 
00244     return false;
00245 }
00246 
00247 /*
00248  * procsignal_sigusr1_handler - handle SIGUSR1 signal.
00249  */
00250 void
00251 procsignal_sigusr1_handler(SIGNAL_ARGS)
00252 {
00253     int         save_errno = errno;
00254 
00255     if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
00256         HandleCatchupInterrupt();
00257 
00258     if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
00259         HandleNotifyInterrupt();
00260 
00261     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
00262         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
00263 
00264     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
00265         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
00266 
00267     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
00268         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
00269 
00270     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
00271         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
00272 
00273     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
00274         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
00275 
00276     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
00277         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
00278 
00279     latch_sigusr1_handler();
00280 
00281     errno = save_errno;
00282 }