From bc44376b0045a5d718b80079a6ee882c7c7f05e2 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Fri, 27 Jan 2023 03:17:41 +0000 Subject: [PATCH v29 4/4] add kqueue support for PQconnCheck and PQcanConnCheck --- doc/src/sgml/libpq.sgml | 15 ++++++----- doc/src/sgml/postgres-fdw.sgml | 10 ++++---- src/interfaces/libpq/fe-misc.c | 47 +++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 5e07a252ce..56e495b3f1 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2692,13 +2692,14 @@ int PQconnCheck(PGconn *conn); Unlike , this function checks socket - health. This check is performed by polling the socket. This function is - currently available only on systems that support the non-standard - POLLRDHUP extension to the poll system - call, including Linux. returns 1 - if the remote peer seems to be closed, returns 0 if - the socket is valid, and returns -1 if the connection - has been already invalid or an error is error occurred. + health. This check is performed by polling the socket. 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. returns + 1 if the remote peer seems to be closed, returns + 0 if the socket is valid, and returns + -1 if the connection has been already invalid or + an error is error occurred. diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 05265a1ef8..b6912f4d94 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -859,11 +859,11 @@ postgres=# SELECT postgres_fdw_verify_connection_states('loopback1'); by postgres_fdw from the local session to the foreign servers. This check is performed by polling the socket and allows long-running transactions to be aborted sooner if the kernel reports - that the connection is closed. This function is currently available only - on systems that support the non-standard POLLRDHUP - extension to the poll system call, including Linux. This - returns true if all the remote connections are still - valid, or the checking is not supported on this platform. + that the connection is closed. 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. This returns + true if all the remote connections are still valid, + or the checking is not supported on this platform. false is returned if the local session seems to be disconnected from at least one remote server. Example usage of the function: diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 6e807e7c6a..4d493b7091 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -45,6 +45,14 @@ #include #endif +/* We use poll(2) if it has POLLRDHUP event, otherwise kqueue(2) */ +#if (!(defined(HAVE_POLL) && defined(POLLRDHUP)) && \ + defined(HAVE_KQUEUE)) +#include +#include +#include +#endif + #include "libpq-fe.h" #include "libpq-int.h" #include "mb/pg_wchar.h" @@ -1227,6 +1235,7 @@ PQenv2encoding(void) static int pqconnCheck_internal(int sock) { + /* We use poll(2) if it has POLLRDHUP event, otherwise kqueue(2) */ #if (defined(HAVE_POLL) && defined(POLLRDHUP)) struct pollfd input_fd; int errflags = POLLHUP | POLLERR | POLLNVAL; @@ -1249,6 +1258,41 @@ pqconnCheck_internal(int sock) return -1; return input_fd.revents; +#elif defined(HAVE_KQUEUE) + struct kevent kev, ret; + struct timespec timeout = {}; + static int kq = -1; + int result; + + /* If this function has never called yet, create a kernel event queue */ + if (kq < 0) + { + kq = kqueue(); + if (kq < 0) + return -1; + } + + /* Prepare kevent structure */ + EV_SET(&kev, sock, EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(kq, &kev, 1, NULL, 0, NULL)) + return -1; + + /* + * Check the status of socket. Note that we will retry as long as we get + * EINTR. + */ + do + result = kevent(kq, NULL, 0, &ret, 1, &timeout); + while (result < 0 && errno == EINTR); + + /* Clean up the queue */ + EV_SET(&kev, sock, EVFILT_READ, EV_DELETE, 0, 0, NULL); + kevent(kq, &kev, 1, NULL, 0, NULL); + + if (result < 0) + return -1; + + return ret.flags & EV_EOF; #else /* Do not support socket checking on this platform, return 0 */ return 0; @@ -1281,7 +1325,8 @@ PQconnCheck(PGconn *conn) int PQcanConnCheck(void) { -#if (defined(HAVE_POLL) && defined(POLLRDHUP)) +#if ((defined(HAVE_POLL) && defined(POLLRDHUP)) || \ + defined(HAVE_KQUEUE)) return true; #else return false; -- 2.27.0