2009-07-31 20:26:23 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* procsignal.c
|
|
|
|
* Routines for interprocess signalling
|
|
|
|
*
|
|
|
|
*
|
2013-01-01 17:15:01 -05:00
|
|
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
2009-07-31 20:26:23 +00:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/storage/ipc/procsignal.c
|
2009-07-31 20:26:23 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "commands/async.h"
|
|
|
|
#include "miscadmin.h"
|
2011-09-04 01:13:16 -04:00
|
|
|
#include "storage/latch.h"
|
2009-07-31 20:26:23 +00:00
|
|
|
#include "storage/ipc.h"
|
2012-09-20 11:03:04 -03:00
|
|
|
#include "storage/shmem.h"
|
2009-07-31 20:26:23 +00:00
|
|
|
#include "storage/sinval.h"
|
2010-01-16 10:05:59 +00:00
|
|
|
#include "tcop/tcopprot.h"
|
2009-07-31 20:26:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.)
|
|
|
|
*
|
2010-02-26 02:01:40 +00:00
|
|
|
* Each process that wants to receive signals registers its process ID
|
2009-07-31 20:26:23 +00:00
|
|
|
* 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.)
|
2010-02-26 02:01:40 +00:00
|
|
|
*
|
2009-07-31 20:26:23 +00:00
|
|
|
* 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.)
|
|
|
|
*/
|
2010-02-26 02:01:40 +00:00
|
|
|
#define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES)
|
2009-07-31 20:26:23 +00:00
|
|
|
|
|
|
|
static ProcSignalSlot *ProcSignalSlots = NULL;
|
|
|
|
static volatile ProcSignalSlot *MyProcSignalSlot = NULL;
|
|
|
|
|
|
|
|
static bool CheckProcSignal(ProcSignalReason reason);
|
|
|
|
static void CleanupProcSignalState(int status, Datum arg);
|
|
|
|
|
|
|
|
/*
|
2010-08-30 06:33:22 +00:00
|
|
|
* ProcSignalShmemSize
|
2009-07-31 20:26:23 +00:00
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
/*
|
2010-02-26 02:01:40 +00:00
|
|
|
* don't ERROR here. We're exiting anyway, and don't want to get into
|
|
|
|
* infinite loop trying to exit
|
2009-07-31 20:26:23 +00:00
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
2010-02-26 02:01:40 +00:00
|
|
|
int i;
|
2009-07-31 20:26:23 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2010-02-26 02:01:40 +00:00
|
|
|
int save_errno = errno;
|
2009-07-31 20:26:23 +00:00
|
|
|
|
|
|
|
if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
|
|
|
|
HandleCatchupInterrupt();
|
|
|
|
|
|
|
|
if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
|
|
|
|
HandleNotifyInterrupt();
|
|
|
|
|
2010-01-16 10:05:59 +00:00
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
|
|
|
|
|
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
|
|
|
|
|
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
|
|
|
|
|
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
|
|
|
|
|
2010-02-13 01:32:20 +00:00
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
|
|
|
|
|
2010-01-23 16:37:12 +00:00
|
|
|
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
|
|
|
|
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
|
|
|
|
|
Introduce latches. A latch is a boolean variable, with the capability to
wait until it is set. Latches can be used to reliably wait until a signal
arrives, which is hard otherwise because signals don't interrupt select()
on some platforms, and even when they do, there's race conditions.
On Unix, latches use the so called self-pipe trick under the covers to
implement the sleep until the latch is set, without race conditions. On
Windows, Windows events are used.
Use the new latch abstraction to sleep in walsender, so that as soon as
a transaction finishes, walsender is woken up to immediately send the WAL
to the standby. This reduces the latency between master and standby, which
is good.
Preliminary work by Fujii Masao. The latch implementation is by me, with
helpful comments from many people.
2010-09-11 15:48:04 +00:00
|
|
|
latch_sigusr1_handler();
|
|
|
|
|
2009-07-31 20:26:23 +00:00
|
|
|
errno = save_errno;
|
|
|
|
}
|