From b98e5513f33154ede701963ee9115ea056e9dea9 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Wed, 21 Dec 2016 11:01:48 -0500 Subject: [PATCH] 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 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 | 27 +++++++++++++++++++++++++++ src/include/storage/latch.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c index 9def8a12d3d..16d3cad25d2 100644 --- a/src/backend/storage/ipc/latch.c +++ b/src/backend/storage/ipc/latch.c @@ -640,6 +640,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) { @@ -1381,6 +1384,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. * @@ -1464,6 +1479,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)) diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h index 5179ecc0dbd..bce7d114af1 100644 --- a/src/include/storage/latch.h +++ b/src/include/storage/latch.h @@ -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 */ -- 2.30.2