new_latch.patch
text/x-patch
Filename: new_latch.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 1 | 1 |
| src/backend/port/unix_latch.c | 160 | 24 |
| src/backend/port/win32_latch.c | 161 | 18 |
| src/backend/postmaster/fork_process.c | 2 | 0 |
| src/backend/postmaster/pgarch.c | 40 | 14 |
| src/backend/postmaster/postmaster.c | 25 | 0 |
| src/backend/replication/syncrep.c | 1 | 1 |
| src/backend/replication/walsender.c | 3 | 2 |
| src/include/storage/latch.h | 15 | 3 |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index e71090f..b1d38f5 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -10150,7 +10150,7 @@ retry:
/*
* Wait for more WAL to arrive, or timeout to be reached
*/
- WaitLatch(&XLogCtl->recoveryWakeupLatch, 5000000L);
+ WaitLatch(&XLogCtl->recoveryWakeupLatch, WL_LATCH_SET | WL_TIMEOUT, 5000000L);
ResetLatch(&XLogCtl->recoveryWakeupLatch);
}
else
diff --git a/src/backend/port/unix_latch.c b/src/backend/port/unix_latch.c
index 6dae7c9..c60986c 100644
--- a/src/backend/port/unix_latch.c
+++ b/src/backend/port/unix_latch.c
@@ -94,6 +94,7 @@
#include "miscadmin.h"
#include "storage/latch.h"
+#include "storage/pmsignal.h"
#include "storage/shmem.h"
/* Are we currently in WaitLatch? The signal handler would like to know. */
@@ -108,6 +109,15 @@ static void initSelfPipe(void);
static void drainSelfPipe(void);
static void sendSelfPipeByte(void);
+/*
+ * Constants that represent which of a pair of fds given
+ * to pipe() is watched and owned in the context of
+ * dealing with postmaster death
+ */
+#define POSTMASTER_FD_WATCH 0
+#define POSTMASTER_FD_OWN 1
+
+extern int postmaster_alive_fds[2];
/*
* Initialize a backend-local latch.
@@ -188,22 +198,22 @@ DisownLatch(volatile Latch *latch)
* backend-local latch initialized with InitLatch, or a shared latch
* associated with the current process by calling OwnLatch.
*
- * Returns 'true' if the latch was set, or 'false' if timeout was reached.
+ * Returns bit field indicating which condition(s) caused the wake-up.
*/
-bool
-WaitLatch(volatile Latch *latch, long timeout)
+int
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
{
- return WaitLatchOrSocket(latch, PGINVALID_SOCKET, false, false, timeout) > 0;
+ return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
}
/*
* Like WaitLatch, but will also return when there's data available in
- * 'sock' for reading or writing. Returns 0 if timeout was reached,
- * 1 if the latch was set, 2 if the socket became readable or writable.
+ * 'sock' for reading or writing.
+ *
+ * Returns bit field indicating which condition(s) caused the wake-up.
*/
int
-WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
- bool forWrite, long timeout)
+WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, long timeout)
{
struct timeval tv,
*tvp = NULL;
@@ -211,12 +221,13 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
fd_set output_mask;
int rc;
int result = 0;
+ bool found = false;
if (latch->owner_pid != MyProcPid)
elog(ERROR, "cannot wait on a latch owned by another process");
/* Initialize timeout */
- if (timeout >= 0)
+ if (timeout >= 0 && (wakeEvents & WL_TIMEOUT))
{
tv.tv_sec = timeout / 1000000L;
tv.tv_usec = timeout % 1000000L;
@@ -224,7 +235,7 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
}
waiting = true;
- for (;;)
+ do
{
int hifd;
@@ -235,16 +246,30 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
* do that), and the select() will return immediately.
*/
drainSelfPipe();
- if (latch->is_set)
- {
- result = 1;
- break;
+ if (latch->is_set && (wakeEvents & WL_LATCH_SET))
+ {
+ result |= WL_LATCH_SET;
+ found = true;
+ /* Leave loop immediately, avoid blocking again.
+ * Since latch is set, no other factor could have
+ * coincided that could make us wake up
+ * independently of the latch being set, so no
+ * need to worry about having missed something.
+ */
+ break;
}
FD_ZERO(&input_mask);
FD_SET(selfpipe_readfd, &input_mask);
+
+ if (wakeEvents & WL_POSTMASTER_DEATH)
+ {
+ FD_SET(postmaster_alive_fds[POSTMASTER_FD_WATCH], &input_mask);
+ if (postmaster_alive_fds[POSTMASTER_FD_WATCH] > hifd)
+ hifd = postmaster_alive_fds[POSTMASTER_FD_WATCH];
+ }
hifd = selfpipe_readfd;
- if (sock != PGINVALID_SOCKET && forRead)
+ if (sock != PGINVALID_SOCKET && (wakeEvents & WL_SOCKET_READABLE))
{
FD_SET(sock, &input_mask);
if (sock > hifd)
@@ -252,7 +277,7 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
}
FD_ZERO(&output_mask);
- if (sock != PGINVALID_SOCKET && forWrite)
+ if (sock != PGINVALID_SOCKET && (wakeEvents & WL_SOCKET_WRITEABLE))
{
FD_SET(sock, &output_mask);
if (sock > hifd)
@@ -268,20 +293,35 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead,
(errcode_for_socket_access(),
errmsg("select() failed: %m")));
}
- if (rc == 0)
+ if (rc == 0 && (wakeEvents & WL_TIMEOUT))
{
/* timeout exceeded */
- result = 0;
- break;
+ result |= WL_TIMEOUT;
+ found = true;
}
- if (sock != PGINVALID_SOCKET &&
- ((forRead && FD_ISSET(sock, &input_mask)) ||
- (forWrite && FD_ISSET(sock, &output_mask))))
+ if (sock != PGINVALID_SOCKET)
{
- result = 2;
- break; /* data available in socket */
+ if ((wakeEvents & WL_SOCKET_READABLE ) && FD_ISSET(sock, &input_mask))
+ {
+ result |= WL_SOCKET_READABLE;
+ found = true; /* data available in socket */
+ }
+ if ((wakeEvents & WL_SOCKET_WRITEABLE) && FD_ISSET(sock, &output_mask))
+ {
+ result |= WL_SOCKET_WRITEABLE;
+ found = true;
+ }
}
+ if ((wakeEvents & WL_POSTMASTER_DEATH) &&
+ FD_ISSET(postmaster_alive_fds[POSTMASTER_FD_WATCH], &input_mask) &&
+ !PostmasterIsAlive(true))
+ {
+ result |= WL_POSTMASTER_DEATH;
+ found = true;
+ }
}
+ while(!found);
+
waiting = false;
return result;
@@ -430,3 +470,99 @@ drainSelfPipe(void)
elog(ERROR, "unexpected EOF on self-pipe");
}
}
+
+/*
+ * Called once from the postmaster, so that child processes can subsequently
+ * monitor if their parent is dead. We open up an anoymous pipe, and have child
+ * processes block on a select() call that examines if the read file descriptor
+ * is ready for reading. They do so through a latch.
+ *
+ * Child processes are responsible for releasing the death watch handler, so
+ * that only the postmaster holds it, and a select() on the fd returns upon the
+ * one and only holder (the postmaster) dying.
+ *
+ * This is a trick that obviates the need for auxiliary backends to have tight
+ * polling loops where they check if the postmaster is alive. We do this because
+ * that pattern results in an excessive number of wakeups per second when idle.
+ */
+void
+InitPostmasterDeathWatchHandle(void)
+{
+ int flags;
+
+ /*
+ * Create pipe. The postmaster is deemed dead if
+ * no process has the writing end (POSTMASTER_FD_OWN) open.
+ *
+ */
+ Assert(MyProcPid == PostmasterPid);
+ if (pipe(postmaster_alive_fds))
+ {
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg( "pipe() call failed to create pipe to monitor postmaster death: %s", strerror(errno))));
+ }
+
+ flags = fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_GETFL);
+ if (flags < 0)
+ {
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg("Failed to set the postmaster death watching fd's flags: %s", strerror(errno))));
+ }
+
+ /*
+ * Set FNONBLOCK to allow checking for the fd's presence with a read() call
+ * and FASYNC to deliver a signal to our process group if the life sign vanishes
+ */
+ flags |= FNONBLOCK | FASYNC;
+ if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFL, flags))
+ {
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg("Failed to set the postmaster death watching fd's flags: %s", strerror(errno))));
+ }
+
+ /* Send SIGIO signal to the whole process group */
+ if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETOWN, -getpgrp()))
+ {
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg("Failed to set the life sign's watching end's notification pid: %s", strerror(errno))));
+ }
+}
+
+/*
+ * Release postmaster death watch handle.
+ *
+ * Important: This must be called immediately after a process
+ * forks from the postmaster. Otherwise, latch clients will
+ * not wake up on postmaster death, even if they have requested
+ * to.
+ *
+ * Even some hypothetical backend that doesn't care about postmaster
+ * death has a responsibility to call this function - otherwise,
+ * some other latch client backend could wait in vain to be informed
+ * of postmaster death, because the irresponsible backend held open
+ * the ownership file descriptor and outlived the postmaster.
+ *
+ * We call the function within the fork machinery to handle all cases,
+ * so new backends need not bother with this themselves
+ */
+void
+ReleasePostmasterDeathWatchHandle(void)
+{
+ /* MyProcPid won't have been set yet */
+ Assert(PostmasterPid != getpid());
+ /* Please don't ask twice */
+ Assert(postmaster_alive_fds[POSTMASTER_FD_OWN] != -1);
+ /* Release parent's ownership fd - only postmaster should hold it */
+ if (close(postmaster_alive_fds[ POSTMASTER_FD_OWN]))
+ {
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg("Failed to close file descriptor associated with Postmaster death in child process %d", MyProcPid)));
+ }
+ postmaster_alive_fds[POSTMASTER_FD_OWN] = -1;
+}
+
diff --git a/src/backend/port/win32_latch.c b/src/backend/port/win32_latch.c
index 3509302..26c4655 100644
--- a/src/backend/port/win32_latch.c
+++ b/src/backend/port/win32_latch.c
@@ -25,8 +25,16 @@
#include "miscadmin.h"
#include "replication/walsender.h"
#include "storage/latch.h"
+#include "storage/pmsignal.h"
#include "storage/shmem.h"
+static DWORD WINAPI
+death_pipe_thread(LPVOID param);
+static DWORD WINAPI
+block_death_pipe_thread(LPVOID param);
+
+static volatile Latch *current_latch = NULL;
+static volatile bool postmasted_died = false;
void
InitLatch(volatile Latch *latch)
@@ -81,15 +89,14 @@ DisownLatch(volatile Latch *latch)
latch->owner_pid = 0;
}
-bool
-WaitLatch(volatile Latch *latch, long timeout)
+int
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
{
- return WaitLatchOrSocket(latch, PGINVALID_SOCKET, false, false, timeout) > 0;
+ return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
}
int
-WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
- bool forWrite, long timeout)
+WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, SOCKET sock, long timeout)
{
DWORD rc;
HANDLE events[3];
@@ -97,19 +104,20 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
HANDLE sockevent = WSA_INVALID_EVENT; /* silence compiler */
int numevents;
int result = 0;
+ bool found = false;
latchevent = latch->event;
events[0] = latchevent;
events[1] = pgwin32_signal_event;
numevents = 2;
- if (sock != PGINVALID_SOCKET && (forRead || forWrite))
+ if (sock != PGINVALID_SOCKET && ((wakeEvents & WL_SOCKET_READABLE) || (wakeEvents & WL_SOCKET_WRITEABLE)))
{
int flags = 0;
- if (forRead)
+ if (wakeEvents & WL_SOCKET_READABLE)
flags |= FD_READ;
- if (forWrite)
+ if (wakeEvents & WL_SOCKET_WRITEABLE)
flags |= FD_WRITE;
sockevent = WSACreateEvent();
@@ -117,8 +125,9 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
events[numevents++] = sockevent;
}
- for (;;)
+ do
{
+ HANDLE pipe_monitor_thread = NULL;
/*
* Reset the event, and check if the latch is set already. If someone
* sets the latch between this and the WaitForMultipleObjects() call
@@ -129,18 +138,52 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
elog(ERROR, "ResetEvent failed: error code %d", (int) GetLastError());
if (latch->is_set)
{
- result = 1;
+ result |= WL_LATCH_SET;
+ found = true;
+ /* Leave loop immediately, avoid blocking again.
+ * Since latch is set, no other factor could have
+ * coincided that could make us wake up
+ * independently of the latch being set, so no
+ * need to worry about having missed something.
+ */
break;
}
+ if (wakeEvents & WL_POSTMASTER_DEATH)
+ {
+ current_latch = latch;
+ /* Start thread to monitor postmaster death
+ * independently of monitoring everything else
+ * in WaitForMultipleObjects() call below
+ */
+ pipe_monitor_thread = CreateThread(NULL, 0, block_death_pipe_thread, NULL, 0, NULL);
+ Assert(pipe_monitor_thread);
+ if (pipe_monitor_thread == NULL)
+ ereport(FATAL,
+ (errmsg_internal("failed to create postmaster death monitoring thread")));
+ }
+
rc = WaitForMultipleObjects(numevents, events, FALSE,
(timeout >= 0) ? (timeout / 1000) : INFINITE);
- if (rc == WAIT_FAILED)
+
+ if (wakeEvents & WL_POSTMASTER_DEATH)
+ TerminateThread(pipe_monitor_thread, 0);
+
+
+ if ( (wakeEvents & WL_POSTMASTER_DEATH) &&
+ postmasted_died &&
+ !PostmasterIsAlive(true))
+ {
+ /* Postmaster died */
+ result |= WL_POSTMASTER_DEATH;
+ found = true;
+ }
+ else if (rc == WAIT_FAILED)
elog(ERROR, "WaitForMultipleObjects() failed: error code %d", (int) GetLastError());
else if (rc == WAIT_TIMEOUT)
{
- result = 0;
- break;
+ result |= WL_TIMEOUT;
+ found = true;
}
else if (rc == WAIT_OBJECT_0 + 1)
pgwin32_dispatch_queued_signals();
@@ -155,17 +198,24 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
ereport(FATAL,
(errmsg_internal("failed to enumerate network events: %i", (int) GetLastError())));
- if ((forRead && resEvents.lNetworkEvents & FD_READ) ||
- (forWrite && resEvents.lNetworkEvents & FD_WRITE))
- result = 2;
- break;
+ if ((wakeEvents & WL_SOCKET_READABLE ) && (resEvents.lNetworkEvents & FD_READ))
+ {
+ result |= WL_SOCKET_READABLE;
+ found = true;
+ }
+ if ((wakeEvents & WL_SOCKET_WRITEABLE) && (resEvents.lNetworkEvents & FD_WRITE))
+ {
+ result |= WL_SOCKET_WRITEABLE;
+ found = true;
+ }
}
else if (rc != WAIT_OBJECT_0)
elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %d", (int) rc);
}
+ while(!found);
/* Clean up the handle we created for the socket */
- if (sock != PGINVALID_SOCKET && (forRead || forWrite))
+ if (sock != PGINVALID_SOCKET && ((wakeEvents & WL_SOCKET_READABLE) || (wakeEvents & WL_SOCKET_WRITEABLE)))
{
WSAEventSelect(sock, sockevent, 0);
WSACloseEvent(sockevent);
@@ -214,3 +264,96 @@ ResetLatch(volatile Latch *latch)
{
latch->is_set = false;
}
+
+/*
+ * Called once from the postmaster, so that its child processes can
+ * subsequently monitor if their parent is dead.
+ *
+ * Spawns a thread that creates pipe instances as needed for child processes
+ * to block on through ReadFile() calls. If the call ever returns, the
+ * postmaster is dead.
+ */
+void
+InitPostmasterDeathWatchHandle(void)
+{
+ HANDLE pipe_spawn_thread;
+ /* Create thread for spawning pipe instances as needed */
+ Assert(MyProcPid == PostmasterPid);
+ pipe_spawn_thread = CreateThread(NULL, 0, death_pipe_thread, NULL, 0, NULL);
+
+ if (pipe_spawn_thread == NULL)
+ ereport(FATAL,
+ (errmsg_internal("failed to create postmaster death watch handler thread")));
+}
+
+/* Postmaster death handling thread */
+static DWORD WINAPI
+death_pipe_thread(LPVOID param)
+{
+ char pipename[128];
+ snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pm_death_%d", PostmasterPid);
+
+ for(;;)
+ {
+ BOOL success;
+ HANDLE pipe;
+
+ pipe = CreateNamedPipe(pipename,
+ PIPE_ACCESS_OUTBOUND | FILE_FLAG_WRITE_THROUGH,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL);
+
+ if (pipe == INVALID_HANDLE_VALUE)
+ {
+ write_stderr("CreateNamedPipe failed when creating death pipe, GetLastError(): %d\n", (int) GetLastError());
+ return -1;
+ }
+
+ success = ConnectNamedPipe(pipe, NULL);
+ if (!success)
+ {
+ write_stderr("ConnectNamedPipe failed when creating death pipe, GetLastError(): %d\n", (int) GetLastError());
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Postmaster death pipe blocking thread */
+static DWORD WINAPI
+block_death_pipe_thread(LPVOID param)
+{
+ char buf[4096];
+ DWORD cbRead;
+ HANDLE pipe;
+ char pipename[128];
+
+ snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pm_death_%d", PostmasterPid);
+ postmasted_died = false;
+
+ pipe = CreateFile(
+ pipename,
+ GENERIC_READ, /* read and write access */
+ FILE_SHARE_READ, /* read sharing */
+ NULL, /* default security attributes */
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ /* Block on call to ReadFile */
+ ReadFile(
+ pipe,
+ buf, /* buffer to receive reply */
+ 4096, /* size of buffer */
+ &cbRead, /* number of bytes read */
+ NULL); /* not overlapped */
+
+ /* If control ever reaches here, the postmaster died */
+ postmasted_died = true;
+ /* We've set postmaster_died; thus, this won't be
+ * spuriously handled as a regular case of
+ * calling SetLatch()
+ */
+ SetLatch(current_latch);
+ return 0;
+}
+
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
index b2fe9a1..6e2f37a 100644
--- a/src/backend/postmaster/fork_process.c
+++ b/src/backend/postmaster/fork_process.c
@@ -11,6 +11,7 @@
*/
#include "postgres.h"
#include "postmaster/fork_process.h"
+#include "storage/latch.h"
#include <fcntl.h>
#include <time.h>
@@ -61,6 +62,7 @@ fork_process(void)
#ifdef LINUX_PROFILE
setitimer(ITIMER_PROF, &prof_itimer, NULL);
#endif
+ ReleasePostmasterDeathWatchHandle();
/*
* By default, Linux tends to kill the postmaster in out-of-memory
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index b40375a..1548a3b 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -40,6 +40,7 @@
#include "postmaster/postmaster.h"
#include "storage/fd.h"
#include "storage/ipc.h"
+#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "utils/guc.h"
@@ -87,6 +88,12 @@ static volatile sig_atomic_t got_SIGTERM = false;
static volatile sig_atomic_t wakened = false;
static volatile sig_atomic_t ready_to_stop = false;
+/*
+ * Latch that archiver loop waits on until it is awakened by
+ * signals, each of which there is a handler for
+ */
+static volatile Latch mainloop_latch;
+
/* ----------
* Local function forward declarations
* ----------
@@ -228,6 +235,8 @@ PgArchiverMain(int argc, char *argv[])
MyProcPid = getpid(); /* reset MyProcPid */
+ InitLatch(&mainloop_latch); /* initialise latch used in main loop, now that we are a subprocess */
+
MyStartTime = time(NULL); /* record Start Time for logging */
/*
@@ -282,6 +291,8 @@ ArchSigHupHandler(SIGNAL_ARGS)
{
/* set flag to re-read config file at next convenient time */
got_SIGHUP = true;
+ /* Let the waiting loop iterate */
+ SetLatch(&mainloop_latch);
}
/* SIGTERM signal handler for archiver process */
@@ -295,6 +306,8 @@ ArchSigTermHandler(SIGNAL_ARGS)
* archive commands.
*/
got_SIGTERM = true;
+ /* Let the waiting loop iterate */
+ SetLatch(&mainloop_latch);
}
/* SIGUSR1 signal handler for archiver process */
@@ -303,6 +316,8 @@ pgarch_waken(SIGNAL_ARGS)
{
/* set flag that there is work to be done */
wakened = true;
+ /* Let the waiting loop iterate */
+ SetLatch(&mainloop_latch);
}
/* SIGUSR2 signal handler for archiver process */
@@ -311,6 +326,8 @@ pgarch_waken_stop(SIGNAL_ARGS)
{
/* set flag to do a final cycle and shut down afterwards */
ready_to_stop = true;
+ /* Let the waiting loop iterate */
+ SetLatch(&mainloop_latch);
}
/*
@@ -334,6 +351,13 @@ pgarch_MainLoop(void)
do
{
+ /*
+ * There shouldn't be anything for the archiver to do except to wait
+ * on a latch ... however, the archiver exists to protect our data,
+ * so she wakes up occasionally to allow herself to be proactive.
+ */
+ ResetLatch(&mainloop_latch);
+
/* When we get SIGUSR2, we do one more archive cycle, then exit */
time_to_stop = ready_to_stop;
@@ -370,26 +394,28 @@ pgarch_MainLoop(void)
last_copy_time = time(NULL);
}
- /*
- * There shouldn't be anything for the archiver to do except to wait
- * for a signal ... however, the archiver exists to protect our data,
- * so she wakes up occasionally to allow herself to be proactive.
+ /*
+ * Wait on latch, until various signals are received, or
+ * until a poll will be forced by PGARCH_AUTOWAKE_INTERVAL
+ * having passed since last_copy_time, or on the postmaster's
+ * untimely demise.
*
- * On some platforms, signals won't interrupt the sleep. To ensure we
- * respond reasonably promptly when someone signals us, break down the
- * sleep into 1-second increments, and check for interrupts after each
- * nap.
+ * The caveat about signals resetting the timeout of
+ * WaitLatch()/select() on some platforms can be safely disregarded,
+ * because we handle all expected signals, and all handlers
+ * call SetLatch() where that matters anyway
*/
- while (!(wakened || ready_to_stop || got_SIGHUP ||
- !PostmasterIsAlive(true)))
- {
- time_t curtime;
- pg_usleep(1000000L);
+ if (!time_to_stop) /* Don't wait during last iteration */
+ {
+ time_t curtime = time(NULL);
+ unsigned int timeout_secs = (unsigned int) PGARCH_AUTOWAKE_INTERVAL -
+ (unsigned int) (curtime - last_copy_time);
+ WaitLatch(&mainloop_latch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, timeout_secs * 1000000L);
curtime = time(NULL);
if ((unsigned int) (curtime - last_copy_time) >=
(unsigned int) PGARCH_AUTOWAKE_INTERVAL)
- wakened = true;
+ wakened = true; /* wakened by timeout - this wasn't a SIGHUP, etc */
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 1e2aa9f..a010c11 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -356,6 +356,7 @@ static void RandomSalt(char *md5Salt);
static void signal_child(pid_t pid, int signal);
static bool SignalSomeChildren(int signal, int targets);
+
#define SignalChildren(sig) SignalSomeChildren(sig, BACKEND_TYPE_ALL)
/*
@@ -443,6 +444,7 @@ typedef struct
HANDLE syslogPipe[2];
#else
int syslogPipe[2];
+ int postmaster_alive_fds[2];
#endif
char my_exec_path[MAXPGPATH];
char pkglib_path[MAXPGPATH];
@@ -472,6 +474,15 @@ static void ShmemBackendArrayRemove(Backend *bn);
#define EXIT_STATUS_0(st) ((st) == 0)
#define EXIT_STATUS_1(st) (WIFEXITED(st) && WEXITSTATUS(st) == 1)
+/*
+ * 2 file descriptors that represent postmaster lifesign.
+ * First is LIFESIGN_FD_WATCH, second is LIFESIGN_FD_OWN.
+ * (macros defined in unix_latch.c)
+ */
+#ifndef WIN32
+int postmaster_alive_fds[2];
+#endif
+
/*
* Postmaster main entry point
@@ -492,6 +503,13 @@ PostmasterMain(int argc, char *argv[])
IsPostmasterEnvironment = true;
/*
+ * Initialise mechanism that allows waiting latch clients
+ * to wake on postmaster death, to finish their
+ * remaining business
+ */
+ InitPostmasterDeathWatchHandle();
+
+ /*
* for security, no dir or file created can be group or other accessible
*/
umask(S_IRWXG | S_IRWXO);
@@ -4753,6 +4771,9 @@ save_backend_variables(BackendParameters *param, Port *port,
memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe));
+#ifndef WIN32
+ memcpy(¶m->postmaster_alive_fds, &postmaster_alive_fds, sizeof(postmaster_alive_fds));
+#endif
strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
@@ -4968,6 +4989,10 @@ restore_backend_variables(BackendParameters *param, Port *port)
memcpy(&syslogPipe, ¶m->syslogPipe, sizeof(syslogPipe));
+#ifndef WIN32
+ memcpy(&postmaster_alive_fds, ¶m->postmaster_alive_fds, sizeof(postmaster_alive_fds));
+#endif
+
strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 1d4df8a..efda804 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -171,7 +171,7 @@ SyncRepWaitForLSN(XLogRecPtr XactCommitLSN)
* postmaster death regularly while waiting. Note that timeout here
* does not necessarily release from loop.
*/
- WaitLatch(&MyProc->waitLatch, 60000000L);
+ WaitLatch(&MyProc->waitLatch, WL_LATCH_SET | WL_TIMEOUT, 60000000L);
/* Must reset the latch before testing state. */
ResetLatch(&MyProc->waitLatch);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 470e6d1..27cc350 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -805,8 +805,9 @@ WalSndLoop(void)
}
/* Sleep */
- WaitLatchOrSocket(&MyWalSnd->latch, MyProcPort->sock,
- true, pq_is_send_pending(),
+ WaitLatchOrSocket(&MyWalSnd->latch,
+ WL_LATCH_SET | WL_SOCKET_READABLE | (pq_is_send_pending()? WL_SOCKET_WRITEABLE:0) | WL_TIMEOUT,
+ MyProcPort->sock,
sleeptime * 1000L);
/* Check for replication timeout */
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 03ec071..5418c73 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -38,11 +38,11 @@ extern void InitLatch(volatile Latch *latch);
extern void InitSharedLatch(volatile Latch *latch);
extern void OwnLatch(volatile Latch *latch);
extern void DisownLatch(volatile Latch *latch);
-extern bool WaitLatch(volatile Latch *latch, long timeout);
-extern int WaitLatchOrSocket(volatile Latch *latch, pgsocket sock,
- bool forRead, bool forWrite, long timeout);
+extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, long timeout);
extern void SetLatch(volatile Latch *latch);
extern void ResetLatch(volatile Latch *latch);
+extern void InitPostmasterDeathWatchHandle(void);
#define TestLatch(latch) (((volatile Latch *) latch)->is_set)
@@ -52,8 +52,20 @@ extern void ResetLatch(volatile Latch *latch);
*/
#ifndef WIN32
extern void latch_sigusr1_handler(void);
+/*
+ * On Unix, it is necessary to call ReleasePostmasterDeathWatchHandle()
+ * after forking from PM
+ */
+extern void ReleasePostmasterDeathWatchHandle(void);
#else
#define latch_sigusr1_handler()
#endif
+/* Bitmasks for events that may wake-up WaitLatch() clients */
+#define WL_LATCH_SET (1 << 0)
+#define WL_SOCKET_READABLE (1 << 1)
+#define WL_SOCKET_WRITEABLE (1 << 2)
+#define WL_TIMEOUT (1 << 3)
+#define WL_POSTMASTER_DEATH (1 << 4)
+
#endif /* LATCH_H */