Fix corner-case bug in WaitEventSetWaitBlock on Windows.

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 98a64d0bd7,
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.
This commit is contained in:
Robert Haas 2016-12-21 11:01:48 -05:00
parent 59649c3f1c
commit 3b790d256f
2 changed files with 30 additions and 0 deletions

View 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))

View 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 */