diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2011-07-08 18:27:49 +0300 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2011-07-08 18:44:07 +0300 |
commit | 89fd72cbf26f5d2e3d86ab19c1ead73ab8fac0fe (patch) | |
tree | fa0605f7ec1ff3a798b1c22fd847185158288a8b /src/backend/port/unix_latch.c | |
parent | 9598afa3b0f7a7fdcf3740173346950b2bd5942c (diff) | |
download | postgresql-89fd72cbf26f5d2e3d86ab19c1ead73ab8fac0fe.tar.gz postgresql-89fd72cbf26f5d2e3d86ab19c1ead73ab8fac0fe.zip |
Introduce a pipe between postmaster and each backend, which can be used to
detect postmaster death. Postmaster keeps the write-end of the pipe open,
so when it dies, children get EOF in the read-end. That can conveniently
be waited for in select(), which allows eliminating some of the polling
loops that check for postmaster death. This patch doesn't yet change all
the loops to use the new mechanism, expect a follow-on patch to do that.
This changes the interface to WaitLatch, so that it takes as argument a
bitmask of events that it waits for. Possible events are latch set, timeout,
postmaster death, and socket becoming readable or writeable.
The pipe method behaves slightly differently from the kill() method
previously used in PostmasterIsAlive() in the case that postmaster has died,
but its parent has not yet read its exit code with waitpid(). The pipe
returns EOF as soon as the process dies, but kill() continues to return
true until waitpid() has been called (IOW while the process is a zombie).
Because of that, change PostmasterIsAlive() to use the pipe too, otherwise
WaitLatch() would return immediately with WL_POSTMASTER_DEATH, while
PostmasterIsAlive() would claim it's still alive. That could easily lead to
busy-waiting while postmaster is in zombie state.
Peter Geoghegan with further changes by me, reviewed by Fujii Masao and
Florian Pflug.
Diffstat (limited to 'src/backend/port/unix_latch.c')
-rw-r--r-- | src/backend/port/unix_latch.c | 101 |
1 files changed, 70 insertions, 31 deletions
diff --git a/src/backend/port/unix_latch.c b/src/backend/port/unix_latch.c index 6dae7c94c03..9940a42e33c 100644 --- a/src/backend/port/unix_latch.c +++ b/src/backend/port/unix_latch.c @@ -93,6 +93,7 @@ #endif #include "miscadmin.h" +#include "postmaster/postmaster.h" #include "storage/latch.h" #include "storage/shmem.h" @@ -176,34 +177,44 @@ DisownLatch(volatile Latch *latch) } /* - * Wait for given latch to be set or until timeout is exceeded. - * If the latch is already set, the function returns immediately. + * Wait for a given latch to be set, postmaster death, or until timeout is + * exceeded. 'wakeEvents' is a bitmask that specifies which of those events + * to wait for. If the latch is already set (and WL_LATCH_SET is given), the + * function returns immediately. * - * The 'timeout' is given in microseconds, and -1 means wait forever. - * On some platforms, signals cause the timeout to be restarted, so beware - * that the function can sleep for several times longer than the specified - * timeout. + * The 'timeout' is given in microseconds. It must be >= 0 if WL_TIMEOUT + * event is given, otherwise it is ignored. On some platforms, signals cause + * the timeout to be restarted, so beware that the function can sleep for + * several times longer than the specified timeout. * * The latch must be owned by the current process, ie. it must be a * 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. Note + * that if multiple wake-up conditions are true, there is no guarantee that + * we return all of them in one call, but we will return at least one. Also, + * according to the select(2) man page on Linux, select(2) may spuriously + * return and report a file descriptor as readable, when it's not. We use + * select(2), so WaitLatch can also spuriously claim that a socket is + * readable, or postmaster has died, even when none of the wake conditions + * have been satisfied. That should be rare in practice, but the caller + * should not use the return value for anything critical, re-checking the + * situation with PostmasterIsAlive() or read() on a socket if necessary. */ -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. + * Like WaitLatch, but with an extra socket argument for WL_SOCKET_* + * conditions. */ 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; @@ -212,19 +223,26 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead, int rc; int result = 0; - if (latch->owner_pid != MyProcPid) + /* Ignore WL_SOCKET_* events if no valid socket is given */ + if (sock == PGINVALID_SOCKET) + wakeEvents &= ~(WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE); + + Assert(wakeEvents != 0); /* must have at least one wake event */ + + if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid) elog(ERROR, "cannot wait on a latch owned by another process"); /* Initialize timeout */ - if (timeout >= 0) + if (wakeEvents & WL_TIMEOUT) { + Assert(timeout >= 0); tv.tv_sec = timeout / 1000000L; tv.tv_usec = timeout % 1000000L; tvp = &tv; } waiting = true; - for (;;) + do { int hifd; @@ -235,16 +253,28 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead, * do that), and the select() will return immediately. */ drainSelfPipe(); - if (latch->is_set) + if ((wakeEvents & WL_LATCH_SET) && latch->is_set) { - result = 1; + result |= WL_LATCH_SET; + /* + * Leave loop immediately, avoid blocking again. We don't attempt + * to report any other events that might also be satisfied. + */ break; } FD_ZERO(&input_mask); FD_SET(selfpipe_readfd, &input_mask); hifd = selfpipe_readfd; - if (sock != PGINVALID_SOCKET && forRead) + + 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]; + } + + if (wakeEvents & WL_SOCKET_READABLE) { FD_SET(sock, &input_mask); if (sock > hifd) @@ -252,14 +282,17 @@ WaitLatchOrSocket(volatile Latch *latch, pgsocket sock, bool forRead, } FD_ZERO(&output_mask); - if (sock != PGINVALID_SOCKET && forWrite) + if (wakeEvents & WL_SOCKET_WRITEABLE) { FD_SET(sock, &output_mask); if (sock > hifd) hifd = sock; } + /* Sleep */ rc = select(hifd + 1, &input_mask, &output_mask, NULL, tvp); + + /* Check return code */ if (rc < 0) { if (errno == EINTR) @@ -268,20 +301,26 @@ 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; } - if (sock != PGINVALID_SOCKET && - ((forRead && FD_ISSET(sock, &input_mask)) || - (forWrite && FD_ISSET(sock, &output_mask)))) + if ((wakeEvents & WL_SOCKET_READABLE) && FD_ISSET(sock, &input_mask)) { - result = 2; - break; /* data available in socket */ + /* data available in socket */ + result |= WL_SOCKET_READABLE; } - } + if ((wakeEvents & WL_SOCKET_WRITEABLE) && FD_ISSET(sock, &output_mask)) + { + result |= WL_SOCKET_WRITEABLE; + } + if ((wakeEvents & WL_POSTMASTER_DEATH) && + FD_ISSET(postmaster_alive_fds[POSTMASTER_FD_WATCH], &input_mask)) + { + result |= WL_POSTMASTER_DEATH; + } + } while(result == 0); waiting = false; return result; |