]> granicus.if.org Git - postgresql/commitdiff
Fix WaitEventSetWait() to handle write-ready waits properly on Windows.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 17 Mar 2017 18:58:06 +0000 (14:58 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 17 Mar 2017 18:58:06 +0000 (14:58 -0400)
Windows apparently will not detect socket write-ready events unless a
preceding send attempt returned WSAEWOULDBLOCK.  In many usage patterns
that's satisfied by the caller of WaitEvenSetWait(), but not always.

Apply the same solution that we already had in pgwin32_select(), namely to
perform a dummy WSASend() call with len=0.  This will return WSAEWOULDBLOCK
if there's no buffer space (even though it could legitimately do nothing
and report success, which makes me a bit nervous about this solution;
but since it's been working fine in libpq, let's roll with it).

In passing, improve the comments about this in pgwin32_select(), and remove
duplicated code there.

Back-patch to 9.6 where WaitEventSetWait() was introduced.  We might need
to back-patch something similar into predecessor code.  But given the lack
of complaints so far, it's not clear that the case ever gets exercised
in the back branches, so I'm not going to expend effort on it right now.

This should resolve recurring failures on buildfarm member bowerbird,
which has been failing since 1e8a85009 went in.

Diagnosis and patch by Petr Jelinek, cosmetic adjustments by me.

Discussion: https://postgr.es/m/5b6a6d6d-fb45-0afb-2e95-5600063c3dbd@2ndquadrant.com

src/backend/port/win32/socket.c
src/backend/storage/ipc/latch.c

index a6f9070761a83ebf4a13a94d4c54247cf8cf7ccf..f11d6e6eb86f6a5b7ade872843902bf1de903544 100644 (file)
@@ -523,11 +523,16 @@ pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, c
        FD_ZERO(&outwritefds);
 
        /*
-        * Write FDs are different in the way that it is only flagged by
-        * WSASelectEvent() if we have tried to write to them first. So try an
-        * empty write
+        * Windows does not guarantee to log an FD_WRITE network event indicating
+        * that more data can be sent unless the previous send() failed with
+        * WSAEWOULDBLOCK.  While our caller might well have made such a call, we
+        * cannot assume that here.  Therefore, if waiting for write-ready, force
+        * the issue by doing a dummy send().  If the dummy send() succeeds,
+        * assume that the socket is in fact write-ready, and return immediately.
+        * Also, if it fails with something other than WSAEWOULDBLOCK, return a
+        * write-ready indication to let our caller deal with the error condition.
         */
-       if (writefds)
+       if (writefds != NULL)
        {
                for (i = 0; i < writefds->fd_count; i++)
                {
@@ -539,20 +544,11 @@ pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, c
                        buf.len = 0;
 
                        r = WSASend(writefds->fd_array[i], &buf, 1, &sent, 0, NULL, NULL);
-                       if (r == 0)                     /* Completed - means things are fine! */
+                       if (r == 0 || WSAGetLastError() != WSAEWOULDBLOCK)
                                FD_SET(writefds->fd_array[i], &outwritefds);
-
-                       else
-                       {                                       /* Not completed */
-                               if (WSAGetLastError() != WSAEWOULDBLOCK)
-
-                                       /*
-                                        * Not completed, and not just "would block", so an error
-                                        * occurred
-                                        */
-                                       FD_SET(writefds->fd_array[i], &outwritefds);
-                       }
                }
+
+               /* If we found any write-ready sockets, just return them immediately */
                if (outwritefds.fd_count > 0)
                {
                        memcpy(writefds, &outwritefds, sizeof(fd_set));
index ea7f930866fbb5eaaf636d241c35afd1cb64c3bc..47983704d7ed4a211e66c28d957c76237ec0115a 100644 (file)
@@ -1401,6 +1401,38 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
                        WaitEventAdjustWin32(set, cur_event);
                        cur_event->reset = false;
                }
+
+               /*
+                * Windows does not guarantee to log an FD_WRITE network event
+                * indicating that more data can be sent unless the previous send()
+                * failed with WSAEWOULDBLOCK.  While our caller might well have made
+                * such a call, we cannot assume that here.  Therefore, if waiting for
+                * write-ready, force the issue by doing a dummy send().  If the dummy
+                * send() succeeds, assume that the socket is in fact write-ready, and
+                * return immediately.  Also, if it fails with something other than
+                * WSAEWOULDBLOCK, return a write-ready indication to let our caller
+                * deal with the error condition.
+                */
+               if (cur_event->events & WL_SOCKET_WRITEABLE)
+               {
+                       char            c;
+                       WSABUF          buf;
+                       DWORD           sent;
+                       int                     r;
+
+                       buf.buf = &c;
+                       buf.len = 0;
+
+                       r = WSASend(cur_event->fd, &buf, 1, &sent, 0, NULL, NULL);
+                       if (r == 0 || WSAGetLastError() != WSAEWOULDBLOCK)
+                       {
+                               occurred_events->pos = cur_event->pos;
+                               occurred_events->user_data = cur_event->user_data;
+                               occurred_events->events = WL_SOCKET_WRITEABLE;
+                               occurred_events->fd = cur_event->fd;
+                               return 1;
+                       }
+               }
        }
 
        /*