]> granicus.if.org Git - postgresql/commitdiff
Only clear latch self-pipe/event if there is a pending notification.
authorAndres Freund <andres@anarazel.de>
Fri, 18 Mar 2016 18:43:59 +0000 (11:43 -0700)
committerAndres Freund <andres@anarazel.de>
Fri, 18 Mar 2016 18:47:05 +0000 (11:47 -0700)
This avoids a good number of, individually quite fast, system calls in
scenarios with many quick queries. Besides the aesthetic benefit of
seing fewer superflous system calls with strace, it also improves
performance by ~2% measured by pgbench -M prepared -c 96 -j 8 -S (scale
100).

Without having benchmarked it, this patch also adjust the windows code,
as that makes it easier to unify the unix/windows codepaths in a later
patch. There's little reason to diverge in behaviour between the
platforms.

Discussion: CA+TgmoYc1Zm+Szoc_Qbzi92z2c1vRHZmjhfPn5uC=w8bXv6Avg@mail.gmail.com
Reviewed-By: Robert Haas
src/backend/port/unix_latch.c
src/backend/port/win32_latch.c

index e7be7ec11797d720ba17566d9e54a9a125ce2f15..104401d0feb11122c023b60490b2d30434bc338c 100644 (file)
@@ -283,27 +283,31 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
        do
        {
                /*
-                * Clear the pipe, then check if the latch is set already. If someone
-                * sets the latch between this and the poll()/select() below, the
-                * setter will write a byte to the pipe (or signal us and the signal
-                * handler will do that), and the poll()/select() will return
-                * immediately.
+                * Check if the latch is set already. If so, leave loop immediately,
+                * avoid blocking again. We don't attempt to report any other events
+                * that might also be satisfied.
+                *
+                * If someone sets the latch between this and the poll()/select()
+                * below, the setter will write a byte to the pipe (or signal us and
+                * the signal handler will do that), and the poll()/select() will
+                * return immediately.
+                *
+                * If there's a pending byte in the self pipe, we'll notice whenever
+                * blocking. Only clearing the pipe in that case avoids having to
+                * drain it every time WaitLatchOrSocket() is used. Should the
+                * pipe-buffer fill up we're still ok, because the pipe is in
+                * nonblocking mode. It's unlikely for that to happen, because the
+                * self pipe isn't filled unless we're blocking (waiting = true), or
+                * from inside a signal handler in latch_sigusr1_handler().
                 *
                 * Note: we assume that the kernel calls involved in drainSelfPipe()
                 * and SetLatch() will provide adequate synchronization on machines
                 * with weak memory ordering, so that we cannot miss seeing is_set if
                 * the signal byte is already in the pipe when we drain it.
                 */
-               drainSelfPipe();
-
                if ((wakeEvents & WL_LATCH_SET) && latch->is_set)
                {
                        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;
                }
 
@@ -313,24 +317,26 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                 */
 #if defined(LATCH_USE_POLL)
                nfds = 0;
+
+               /* selfpipe is always in pfds[0] */
+               pfds[0].fd = selfpipe_readfd;
+               pfds[0].events = POLLIN;
+               pfds[0].revents = 0;
+               nfds++;
+
                if (wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE))
                {
-                       /* socket, if used, is always in pfds[0] */
-                       pfds[0].fd = sock;
-                       pfds[0].events = 0;
+                       /* socket, if used, is always in pfds[1] */
+                       pfds[1].fd = sock;
+                       pfds[1].events = 0;
                        if (wakeEvents & WL_SOCKET_READABLE)
-                               pfds[0].events |= POLLIN;
+                               pfds[1].events |= POLLIN;
                        if (wakeEvents & WL_SOCKET_WRITEABLE)
-                               pfds[0].events |= POLLOUT;
-                       pfds[0].revents = 0;
+                               pfds[1].events |= POLLOUT;
+                       pfds[1].revents = 0;
                        nfds++;
                }
 
-               pfds[nfds].fd = selfpipe_readfd;
-               pfds[nfds].events = POLLIN;
-               pfds[nfds].revents = 0;
-               nfds++;
-
                if (wakeEvents & WL_POSTMASTER_DEATH)
                {
                        /* postmaster fd, if used, is always in pfds[nfds - 1] */
@@ -364,19 +370,27 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                else
                {
                        /* at least one event occurred, so check revents values */
+
+                       if (pfds[0].revents & POLLIN)
+                       {
+                               /* There's data in the self-pipe, clear it. */
+                               drainSelfPipe();
+                       }
+
                        if ((wakeEvents & WL_SOCKET_READABLE) &&
-                               (pfds[0].revents & POLLIN))
+                               (pfds[1].revents & POLLIN))
                        {
                                /* data available in socket, or EOF/error condition */
                                result |= WL_SOCKET_READABLE;
                        }
                        if ((wakeEvents & WL_SOCKET_WRITEABLE) &&
-                               (pfds[0].revents & POLLOUT))
+                               (pfds[1].revents & POLLOUT))
                        {
                                /* socket is writable */
                                result |= WL_SOCKET_WRITEABLE;
                        }
-                       if (pfds[0].revents & (POLLHUP | POLLERR | POLLNVAL))
+                       if ((wakeEvents & WL_SOCKET_WRITEABLE) &&
+                               (pfds[1].revents & (POLLHUP | POLLERR | POLLNVAL)))
                        {
                                /* EOF/error condition */
                                if (wakeEvents & WL_SOCKET_READABLE)
@@ -468,6 +482,11 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                else
                {
                        /* at least one event occurred, so check masks */
+                       if (FD_ISSET(selfpipe_readfd, &input_mask))
+                       {
+                               /* There's data in the self-pipe, clear it. */
+                               drainSelfPipe();
+                       }
                        if ((wakeEvents & WL_SOCKET_READABLE) && FD_ISSET(sock, &input_mask))
                        {
                                /* data available in socket, or EOF */
@@ -498,6 +517,16 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                }
 #endif   /* LATCH_USE_SELECT */
 
+               /*
+                * Check again whether latch is set, the arrival of a signal/self-byte
+                * might be what stopped our sleep. It's not required for correctness
+                * to signal the latch as being set (we'd just loop if there's no
+                * other event), but it seems good to report an arrived latch asap.
+                * This way we also don't have to compute the current timestamp again.
+                */
+               if ((wakeEvents & WL_LATCH_SET) && latch->is_set)
+                       result |= WL_LATCH_SET;
+
                /* If we're not done, update cur_timeout for next iteration */
                if (result == 0 && (wakeEvents & WL_TIMEOUT))
                {
index b1b071339ee3740651fe5aa5603102510dc055d8..bbf1b24bdf3287d7e58fe1bc262bd30e025cbdb3 100644 (file)
@@ -181,14 +181,11 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
        do
        {
                /*
-                * Reset the event, and check if the latch is set already. If someone
-                * sets the latch between this and the WaitForMultipleObjects() call
-                * below, the setter will set the event and WaitForMultipleObjects()
-                * will return immediately.
+                * The comment in unix_latch.c's equivalent to this applies here as
+                * well. At least after mentally replacing self-pipe with windows
+                * event. There's no danger of overflowing, as "Setting an event that
+                * is already set has no effect.".
                 */
-               if (!ResetEvent(latchevent))
-                       elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
-
                if ((wakeEvents & WL_LATCH_SET) && latch->is_set)
                {
                        result |= WL_LATCH_SET;
@@ -217,9 +214,13 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                else if (rc == WAIT_OBJECT_0 + 1)
                {
                        /*
-                        * Latch is set.  We'll handle that on next iteration of loop, but
-                        * let's not waste the cycles to update cur_timeout below.
+                        * Reset the event.  We'll re-check the, potentially, set latch on
+                        * next iteration of loop, but let's not waste the cycles to
+                        * update cur_timeout below.
                         */
+                       if (!ResetEvent(latchevent))
+                               elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
+
                        continue;
                }
                else if ((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) &&