]> granicus.if.org Git - postgresql/commitdiff
Make WaitLatchOrSocket's timeout detection more robust.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Jul 2015 15:47:13 +0000 (11:47 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Jul 2015 15:47:13 +0000 (11:47 -0400)
In the previous coding, timeout would be noticed and reported only when
poll() or socket() returned zero (or the equivalent behavior on Windows).
Ordinarily that should work well enough, but it seems conceivable that we
could get into a state where poll() always returns a nonzero value --- for
example, if it is noticing a condition on one of the file descriptors that
we do not think is reason to exit the loop.  If that happened, we'd be in a
busy-wait loop that would fail to terminate even when the timeout expires.

We can make this more robust at essentially no cost, by deciding to exit
of our own accord if we compute a zero or negative time-remaining-to-wait.
Previously the code noted this but just clamped the time-remaining to zero,
expecting that we'd detect timeout on the next loop iteration.

Back-patch to 9.2.  While 9.1 had a version of WaitLatchOrSocket, it was
primitive compared to later versions, and did not guarantee reliable
detection of timeouts anyway.  (Essentially, this is a refinement of
commit 3e7fdcffd6f77187, which was back-patched only as far as 9.2.)

src/backend/port/unix_latch.c
src/backend/port/win32_latch.c

index 147e22cee4eee16e9b51b39476d8128e14927365..90ec4f81d9e2bddb5670447b7eed553108c14d3b 100644 (file)
@@ -460,7 +460,8 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                                result |= WL_SOCKET_WRITEABLE;
                        }
                        if ((wakeEvents & WL_POSTMASTER_DEATH) &&
-                       FD_ISSET(postmaster_alive_fds[POSTMASTER_FD_WATCH], &input_mask))
+                               FD_ISSET(postmaster_alive_fds[POSTMASTER_FD_WATCH],
+                                                &input_mask))
                        {
                                /*
                                 * According to the select(2) man page on Linux, select(2) may
@@ -479,17 +480,22 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 #endif   /* HAVE_POLL */
 
                /* If we're not done, update cur_timeout for next iteration */
-               if (result == 0 && cur_timeout >= 0)
+               if (result == 0 && (wakeEvents & WL_TIMEOUT))
                {
                        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;
-
+                       if (cur_timeout <= 0)
+                       {
+                               /* Timeout has expired, no need to continue looping */
+                               result |= WL_TIMEOUT;
+                       }
 #ifndef HAVE_POLL
-                       tv.tv_sec = cur_timeout / 1000L;
-                       tv.tv_usec = (cur_timeout % 1000L) * 1000L;
+                       else
+                       {
+                               tv.tv_sec = cur_timeout / 1000L;
+                               tv.tv_usec = (cur_timeout % 1000L) * 1000L;
+                       }
 #endif
                }
        } while (result == 0);
index ee9526245fd48bcc29ce871bb4c37cd8e2a5884a..0e3aaeec69eae9909ac115d8442c83d4c87cc390 100644 (file)
@@ -265,13 +265,16 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
                        elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %lu", rc);
 
                /* If we're not done, update cur_timeout for next iteration */
-               if (result == 0 && cur_timeout != INFINITE)
+               if (result == 0 && (wakeEvents & WL_TIMEOUT))
                {
                        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;
+                       if (cur_timeout <= 0)
+                       {
+                               /* Timeout has expired, no need to continue looping */
+                               result |= WL_TIMEOUT;
+                       }
                }
        } while (result == 0);