diff options
Diffstat (limited to 'src/backend/port/unix_latch.c')
-rw-r--r-- | src/backend/port/unix_latch.c | 196 |
1 files changed, 118 insertions, 78 deletions
diff --git a/src/backend/port/unix_latch.c b/src/backend/port/unix_latch.c index bbd1810ea53..29ef38226aa 100644 --- a/src/backend/port/unix_latch.c +++ b/src/backend/port/unix_latch.c @@ -48,6 +48,7 @@ #endif #include "miscadmin.h" +#include "portability/instr_time.h" #include "postmaster/postmaster.h" #include "storage/latch.h" #include "storage/pmsignal.h" @@ -176,12 +177,8 @@ DisownLatch(volatile Latch *latch) * function returns immediately. * * The 'timeout' is given in milliseconds. It must be >= 0 if WL_TIMEOUT flag - * is given. On some platforms, signals do not interrupt the wait, or even - * cause the timeout to be restarted, so beware that the function can sleep - * for several times longer than the requested timeout. However, this - * difficulty is not so great as it seems, because the signal handlers for any - * signals that the caller should respond to ought to be programmed to end the - * wait by calling SetLatch. Ideally, the timeout parameter is vestigial. + * is given. Note that some extra overhead is incurred when WL_TIMEOUT is + * given, so avoid using a timeout if possible. * * The latch must be owned by the current process, ie. it must be a * backend-local latch initialized with InitLatch, or a shared latch @@ -211,13 +208,16 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, { int result = 0; int rc; + instr_time start_time, + cur_time; + long cur_timeout; #ifdef HAVE_POLL struct pollfd pfds[3]; int nfds; #else struct timeval tv, - *tvp = NULL; + *tvp; fd_set input_mask; fd_set output_mask; int hifd; @@ -234,21 +234,30 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid) elog(ERROR, "cannot wait on a latch owned by another process"); - /* Initialize timeout */ + /* + * Initialize timeout if requested. We must record the current time so + * that we can determine the remaining timeout if the poll() or select() + * is interrupted. (On some platforms, select() will update the contents + * of "tv" for us, but unfortunately we can't rely on that.) + */ if (wakeEvents & WL_TIMEOUT) { + INSTR_TIME_SET_CURRENT(start_time); Assert(timeout >= 0); + cur_timeout = timeout; + #ifndef HAVE_POLL - tv.tv_sec = timeout / 1000L; - tv.tv_usec = (timeout % 1000L) * 1000L; + tv.tv_sec = cur_timeout / 1000L; + tv.tv_usec = (cur_timeout % 1000L) * 1000L; tvp = &tv; #endif } else { -#ifdef HAVE_POLL - /* make sure poll() agrees there is no timeout */ - timeout = -1; + cur_timeout = -1; + +#ifndef HAVE_POLL + tvp = NULL; #endif } @@ -311,54 +320,62 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, } /* Sleep */ - rc = poll(pfds, nfds, (int) timeout); + rc = poll(pfds, nfds, (int) cur_timeout); /* Check return code */ if (rc < 0) { - if (errno == EINTR) - continue; - waiting = false; - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("poll() failed: %m"))); + /* EINTR is okay, otherwise complain */ + if (errno != EINTR) + { + waiting = false; + ereport(ERROR, + (errcode_for_socket_access(), + errmsg("poll() failed: %m"))); + } } - if (rc == 0 && (wakeEvents & WL_TIMEOUT)) + else if (rc == 0) { /* timeout exceeded */ - result |= WL_TIMEOUT; + if (wakeEvents & WL_TIMEOUT) + result |= WL_TIMEOUT; } - if ((wakeEvents & WL_SOCKET_READABLE) && - (pfds[0].revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL))) + else { - /* data available in socket, or EOF/error condition */ - result |= WL_SOCKET_READABLE; - } - if ((wakeEvents & WL_SOCKET_WRITEABLE) && - (pfds[0].revents & POLLOUT)) - { - result |= WL_SOCKET_WRITEABLE; - } + /* at least one event occurred, so check revents values */ + if ((wakeEvents & WL_SOCKET_READABLE) && + (pfds[0].revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL))) + { + /* data available in socket, or EOF/error condition */ + result |= WL_SOCKET_READABLE; + } + if ((wakeEvents & WL_SOCKET_WRITEABLE) && + (pfds[0].revents & POLLOUT)) + { + result |= WL_SOCKET_WRITEABLE; + } - /* - * We expect a POLLHUP when the remote end is closed, but because we - * don't expect the pipe to become readable or to have any errors - * either, treat those as postmaster death, too. - */ - if ((wakeEvents & WL_POSTMASTER_DEATH) && - (pfds[nfds - 1].revents & (POLLHUP | POLLIN | POLLERR | POLLNVAL))) - { /* - * 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; and presumably so can poll(2). It's not clear - * that the relevant cases would ever apply to the postmaster - * pipe, but since the consequences of falsely returning - * WL_POSTMASTER_DEATH could be pretty unpleasant, we take the - * trouble to positively verify EOF with PostmasterIsAlive(). + * We expect a POLLHUP when the remote end is closed, but because + * we don't expect the pipe to become readable or to have any + * errors either, treat those cases as postmaster death, too. */ - if (!PostmasterIsAlive()) - result |= WL_POSTMASTER_DEATH; + if ((wakeEvents & WL_POSTMASTER_DEATH) && + (pfds[nfds - 1].revents & (POLLHUP | POLLIN | POLLERR | POLLNVAL))) + { + /* + * 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; and presumably so can poll(2). It's not + * clear that the relevant cases would ever apply to the + * postmaster pipe, but since the consequences of falsely + * returning WL_POSTMASTER_DEATH could be pretty unpleasant, + * we take the trouble to positively verify EOF with + * PostmasterIsAlive(). + */ + if (!PostmasterIsAlive()) + result |= WL_POSTMASTER_DEATH; + } } #else /* !HAVE_POLL */ @@ -395,43 +412,66 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, /* Check return code */ if (rc < 0) { - if (errno == EINTR) - continue; - waiting = false; - ereport(ERROR, - (errcode_for_socket_access(), - errmsg("select() failed: %m"))); + /* EINTR is okay, otherwise complain */ + if (errno != EINTR) + { + waiting = false; + ereport(ERROR, + (errcode_for_socket_access(), + errmsg("select() failed: %m"))); + } } - if (rc == 0 && (wakeEvents & WL_TIMEOUT)) + else if (rc == 0) { /* timeout exceeded */ - result |= WL_TIMEOUT; - } - if ((wakeEvents & WL_SOCKET_READABLE) && FD_ISSET(sock, &input_mask)) - { - /* data available in socket, or EOF */ - result |= WL_SOCKET_READABLE; + if (wakeEvents & WL_TIMEOUT) + result |= WL_TIMEOUT; } - if ((wakeEvents & WL_SOCKET_WRITEABLE) && FD_ISSET(sock, &output_mask)) + else { - result |= WL_SOCKET_WRITEABLE; - } - if ((wakeEvents & WL_POSTMASTER_DEATH) && + /* at least one event occurred, so check masks */ + if ((wakeEvents & WL_SOCKET_READABLE) && FD_ISSET(sock, &input_mask)) + { + /* data available in socket, or EOF */ + 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)) - { - /* - * 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; and presumably so can poll(2). It's not clear - * that the relevant cases would ever apply to the postmaster - * pipe, but since the consequences of falsely returning - * WL_POSTMASTER_DEATH could be pretty unpleasant, we take the - * trouble to positively verify EOF with PostmasterIsAlive(). - */ - if (!PostmasterIsAlive()) - result |= WL_POSTMASTER_DEATH; + { + /* + * 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; and presumably so can poll(2). It's not + * clear that the relevant cases would ever apply to the + * postmaster pipe, but since the consequences of falsely + * returning WL_POSTMASTER_DEATH could be pretty unpleasant, + * we take the trouble to positively verify EOF with + * PostmasterIsAlive(). + */ + if (!PostmasterIsAlive()) + result |= WL_POSTMASTER_DEATH; + } } #endif /* HAVE_POLL */ + + /* If we're not done, update cur_timeout for next iteration */ + if (result == 0 && cur_timeout >= 0) + { + INSTR_TIME_SET_CURRENT(cur_time); + INSTR_TIME_SUBTRACT(cur_time, start_time); + cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time); + if (cur_timeout < 0) + cur_timeout = 0; + +#ifndef HAVE_POLL + tv.tv_sec = cur_timeout / 1000L; + tv.tv_usec = (cur_timeout % 1000L) * 1000L; +#endif + } } while (result == 0); waiting = false; |