From bc44376b0045a5d718b80079a6ee882c7c7f05e2 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <kuroda.hayato@fujitsu.com>
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);
 
       <para>
        Unlike <xref linkend="libpq-PQstatus"/>, 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
-       <symbol>POLLRDHUP</symbol> extension to the <symbol>poll</symbol> system
-       call, including Linux. <xref linkend="libpq-PQconnCheck"/> returns <literal>1</literal>
-       if the remote peer seems to be closed, returns <literal>0</literal> if
-       the socket is valid, and returns <literal>-1</literal> 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. <xref linkend="libpq-PQconnCheck"/> returns
+       <literal>1</literal> if the remote peer seems to be closed, returns
+       <literal>0</literal> if the socket is valid, and returns
+       <literal>-1</literal> if the connection has been already invalid or
+       an error is error occurred.
       </para>
      </listitem>
     </varlistentry>
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 <filename>postgres_fdw</filename> 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 <symbol>POLLRDHUP</symbol>
-      extension to the <symbol>poll</symbol> system call, including Linux. This
-      returns <literal>true</literal> 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
+      <literal>true</literal> if all the remote connections are still valid,
+      or the checking is not supported on this platform.
       <literal>false</literal> 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 <poll.h>
 #endif
 
+/* We use poll(2) if it has POLLRDHUP event, otherwise kqueue(2) */
+#if (!(defined(HAVE_POLL) && defined(POLLRDHUP)) && \
+	 defined(HAVE_KQUEUE))
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#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

