]> granicus.if.org Git - postgresql/commitdiff
Fix corner-case bug in WaitEventSetWaitBlock on Windows.
authorRobert Haas <rhaas@postgresql.org>
Wed, 21 Dec 2016 16:01:48 +0000 (11:01 -0500)
committerRobert Haas <rhaas@postgresql.org>
Wed, 21 Dec 2016 16:01:48 +0000 (11:01 -0500)
If we do not reset the FD_READ event, WaitForMultipleObjects won't
return it again again unless we've meanwhile read from the socket,
which is generally true but not guaranteed.  WaitEventSetWaitBlock
itself may fail to return the event to the caller if the latch is
also set, and even if we changed that, the caller isn't obliged to
handle all returned events at once.  On non-Windows systems, the
socket-read event is purely level-triggered, so this issue does
not exist.  To fix, make Windows reset the event when needed.

This bug was introduced by 98a64d0bd713cb89e61bef6432befc4b7b5da59e,
and causes hangs when trying to use the pldebugger extension.

Patch by Amit Kapial.  Reported and tested by Ashutosh Sharma, who
also provided some analysis.  Further analysis by Michael Paquier.

src/backend/storage/ipc/latch.c
src/include/storage/latch.h

index b7e512978315aea0f28a509d5c64fea8ecf54ced..b4ca75ea2f319a813781370abbe1185159f0478e 100644 (file)
@@ -643,6 +643,9 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
        event->fd = fd;
        event->events = events;
        event->user_data = user_data;
+#ifdef WIN32
+       event->reset = false;
+#endif
 
        if (events == WL_LATCH_SET)
        {
@@ -1389,6 +1392,18 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
        DWORD           rc;
        WaitEvent  *cur_event;
 
+       /* Reset any wait events that need it */
+       for (cur_event = set->events;
+                cur_event < (set->events + set->nevents);
+                cur_event++)
+       {
+               if (cur_event->reset)
+               {
+                       WaitEventAdjustWin32(set, cur_event);
+                       cur_event->reset = false;
+               }
+       }
+
        /*
         * Sleep.
         *
@@ -1472,6 +1487,18 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
                {
                        /* data available in socket */
                        occurred_events->events |= WL_SOCKET_READABLE;
+
+                       /*------
+                        * WaitForMultipleObjects doesn't guarantee that a read event will
+                        * be returned if the latch is set at the same time.  Even if it
+                        * did, the caller might drop that event expecting it to reoccur
+                        * on next call.  So, we must force the event to be reset if this
+                        * WaitEventSet is used again in order to avoid an indefinite
+                        * hang.  Refer https://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
+                        * for the behavior of socket events.
+                        *------
+                        */
+                       cur_event->reset = true;
                }
                if ((cur_event->events & WL_SOCKET_WRITEABLE) &&
                        (resEvents.lNetworkEvents & FD_WRITE))
index e96e88f2faa3221cf9533989b35f59694b8737dd..8f5bd515ad6f0b0a834177b8c5ede615b2132b54 100644 (file)
@@ -133,6 +133,9 @@ typedef struct WaitEvent
        uint32          events;                 /* triggered events */
        pgsocket        fd;                             /* socket fd associated with event */
        void       *user_data;          /* pointer provided in AddWaitEventToSet */
+#ifdef WIN32
+       bool            reset;                  /* Is reset of the event required? */
+#endif
 } WaitEvent;
 
 /* forward declaration to avoid exposing latch.c implementation details */