#endif
#include "miscadmin.h"
+#include "portability/instr_time.h"
#include "postmaster/postmaster.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
* 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
{
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;
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
}
}
/* 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 */
/* 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;
#include <unistd.h>
#include "miscadmin.h"
+#include "portability/instr_time.h"
#include "postmaster/postmaster.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
long timeout)
{
DWORD rc;
+ instr_time start_time,
+ cur_time;
+ long cur_timeout;
HANDLE events[4];
HANDLE latchevent;
HANDLE sockevent = WSA_INVALID_EVENT;
if ((wakeEvents & WL_LATCH_SET) && latch->owner_pid != MyProcPid)
elog(ERROR, "cannot wait on a latch owned by another process");
- /* Convert timeout to form used by WaitForMultipleObjects() */
+ /*
+ * Initialize timeout if requested. We must record the current time so
+ * that we can determine the remaining timeout if WaitForMultipleObjects
+ * is interrupted.
+ */
if (wakeEvents & WL_TIMEOUT)
+ {
+ INSTR_TIME_SET_CURRENT(start_time);
Assert(timeout >= 0);
+ cur_timeout = timeout;
+ }
else
- timeout = INFINITE;
+ cur_timeout = INFINITE;
/*
* Construct an array of event handles for WaitforMultipleObjects().
break;
}
- rc = WaitForMultipleObjects(numevents, events, FALSE, timeout);
+ rc = WaitForMultipleObjects(numevents, events, FALSE, cur_timeout);
if (rc == WAIT_FAILED)
elog(ERROR, "WaitForMultipleObjects() failed: error code %lu",
}
else if (rc == WAIT_OBJECT_0 + 1)
{
- /* Latch is set, we'll handle that on next iteration of loop */
+ /*
+ * Latch is set. We'll handle that on next iteration of loop, but
+ * let's not waste the cycles to update cur_timeout below.
+ */
+ continue;
}
else if ((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) &&
rc == WAIT_OBJECT_0 + 2) /* socket is at event slot 2 */
}
else
elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %lu", rc);
- }
- while (result == 0);
+
+ /* If we're not done, update cur_timeout for next iteration */
+ if (result == 0 && cur_timeout != INFINITE)
+ {
+ 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;
+ }
+ } while (result == 0);
/* Clean up the event object we created for the socket */
if (sockevent != WSA_INVALID_EVENT)