Skip to content

Commit 5546a83

Browse files
committed
Fix lost Windows socket EOF events.
Winsock only signals an FD_CLOSE event once if the other end of the socket shuts down gracefully. Because each WaitLatchOrSocket() call constructs and destroys a new event handle every time, with unlucky timing we can lose it and hang. We get away with this only if the other end disconnects non-gracefully, because FD_CLOSE is repeatedly signaled in that case. To fix this design flaw in our Windows socket support fundamentally, we'd probably need to rearchitect it so that a single event handle exists for the lifetime of a socket, or switch to completely different multiplexing or async I/O APIs. That's going to be a bigger job and probably wouldn't be back-patchable. This brute force kludge closes the race by explicitly polling with MSG_PEEK before sleeping. Back-patch to all supported releases. This should hopefully clear up some random build farm and CI hang failures reported over the years. It might also allow us to try using graceful shutdown in more places again (reverted in commit 29992a6) to fix instability in the transmission of FATAL error messages, but that isn't done by this commit. Reported-by: Tom Lane <tgl@sss.pgh.pa.us> Tested-by: Alexander Lakhin <exclusion@gmail.com> Discussion: https://postgr.es/m/176008.1715492071%40sss.pgh.pa.us
1 parent 9f0f72d commit 5546a83

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

src/backend/storage/ipc/latch.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,38 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
19271927
cur_event->reset = false;
19281928
}
19291929

1930+
/*
1931+
* We associate the socket with a new event handle for each
1932+
* WaitEventSet. FD_CLOSE is only generated once if the other end
1933+
* closes gracefully. Therefore we might miss the FD_CLOSE
1934+
* notification, if it was delivered to another event after we stopped
1935+
* waiting for it. Close that race by peeking for EOF after setting
1936+
* up this handle to receive notifications, and before entering the
1937+
* sleep.
1938+
*
1939+
* XXX If we had one event handle for the lifetime of a socket, we
1940+
* wouldn't need this.
1941+
*/
1942+
if (cur_event->events & WL_SOCKET_READABLE)
1943+
{
1944+
char c;
1945+
WSABUF buf;
1946+
DWORD received;
1947+
DWORD flags;
1948+
1949+
buf.buf = &c;
1950+
buf.len = 1;
1951+
flags = MSG_PEEK;
1952+
if (WSARecv(cur_event->fd, &buf, 1, &received, &flags, NULL, NULL) == 0)
1953+
{
1954+
occurred_events->pos = cur_event->pos;
1955+
occurred_events->user_data = cur_event->user_data;
1956+
occurred_events->events = WL_SOCKET_READABLE;
1957+
occurred_events->fd = cur_event->fd;
1958+
return 1;
1959+
}
1960+
}
1961+
19301962
/*
19311963
* Windows does not guarantee to log an FD_WRITE network event
19321964
* indicating that more data can be sent unless the previous send()

0 commit comments

Comments
 (0)