Use WL_SOCKET_CLOSED for client_connection_check_interval.

Previously we used poll() directly to check for a POLLRDHUP event.
Instead, use the WaitEventSet API to poll the socket for
WL_SOCKET_CLOSED, which knows how to detect this condition on many more
operating systems.

Reviewed-by: Zhihong Yu <zyu@yugabyte.com>
Reviewed-by: Maksim Milyutin <milyutinma@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/77def86b27e41f0efcba411460e929ae%40postgrespro.ru
This commit is contained in:
Thomas Munro 2022-02-14 16:29:44 +13:00
parent 50e570a59e
commit cba5b994c9
4 changed files with 29 additions and 31 deletions

View File

@ -1012,9 +1012,9 @@ include_dir 'conf.d'
the kernel reports that the connection is closed.
</para>
<para>
This option is currently available only on systems that support the
non-standard <symbol>POLLRDHUP</symbol> extension to the
<symbol>poll</symbol> system call, including Linux.
This option relies on kernel events exposed by Linux, macOS, illumos
and the BSD family of operating systems, and is not currently available
on other systems.
</para>
<para>
If the value is specified without units, it is taken as milliseconds.

View File

@ -204,7 +204,7 @@ pq_init(void)
(errmsg("could not set socket to nonblocking mode: %m")));
#endif
FeBeWaitSet = CreateWaitEventSet(TopMemoryContext, 3);
FeBeWaitSet = CreateWaitEventSet(TopMemoryContext, FeBeWaitSetNEvents);
socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE,
MyProcPort->sock, NULL, NULL);
latch_pos = AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
@ -1960,33 +1960,33 @@ pq_settcpusertimeout(int timeout, Port *port)
bool
pq_check_connection(void)
{
#if defined(POLLRDHUP)
/*
* POLLRDHUP is a Linux extension to poll(2) to detect sockets closed by
* the other end. We don't have a portable way to do that without
* actually trying to read or write data on other systems. We don't want
* to read because that would be confused by pipelined queries and COPY
* data. Perhaps in future we'll try to write a heartbeat message instead.
*/
struct pollfd pollfd;
WaitEvent events[FeBeWaitSetNEvents];
int rc;
pollfd.fd = MyProcPort->sock;
pollfd.events = POLLOUT | POLLIN | POLLRDHUP;
pollfd.revents = 0;
/*
* It's OK to modify the socket event filter without restoring, because
* all FeBeWaitSet socket wait sites do the same.
*/
ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, NULL);
rc = poll(&pollfd, 1, 0);
if (rc < 0)
retry:
rc = WaitEventSetWait(FeBeWaitSet, 0, events, lengthof(events), 0);
for (int i = 0; i < rc; ++i)
{
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("could not poll socket: %m")));
return false;
if (events[i].events & WL_SOCKET_CLOSED)
return false;
if (events[i].events & WL_LATCH_SET)
{
/*
* A latch event might be preventing other events from being
* reported. Reset it and poll again. No need to restore it
* because no code should expect latches to survive across
* CHECK_FOR_INTERRUPTS().
*/
ResetLatch(MyLatch);
goto retry;
}
}
else if (rc == 1 && (pollfd.revents & (POLLHUP | POLLRDHUP)))
return false;
#endif
return true;
}

View File

@ -12192,14 +12192,11 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
static bool
check_client_connection_check_interval(int *newval, void **extra, GucSource source)
{
#ifndef POLLRDHUP
/* Linux only, for now. See pq_check_connection(). */
if (*newval != 0)
if (!WaitEventSetCanReportClosed() && *newval != 0)
{
GUC_check_errdetail("client_connection_check_interval must be set to 0 on platforms that lack POLLRDHUP.");
GUC_check_errdetail("client_connection_check_interval must be set to 0 on this platform");
return false;
}
#endif
return true;
}

View File

@ -62,6 +62,7 @@ extern WaitEventSet *FeBeWaitSet;
#define FeBeWaitSetSocketPos 0
#define FeBeWaitSetLatchPos 1
#define FeBeWaitSetNEvents 3
extern int StreamServerPort(int family, const char *hostName,
unsigned short portNumber, const char *unixSocketDir,