Expose PQsocketPoll via libpq

This is useful when connecting to a database asynchronously via
PQconnectStart(), since it handles deciding between poll() and
select(), and some of the required boilerplate.

Tristan Partin, reviewed by Gurjeet Singh, Heikki Linnakangas, Jelte
Fennema-Nio, and me.

Discussion: http://postgr.es/m/D08WWCPVHKHN.3QELIKZJ2D9RZ@neon.tech
This commit is contained in:
Robert Haas 2024-04-02 10:15:56 -04:00
parent 3a352df05e
commit f5e4dedfa8
4 changed files with 47 additions and 5 deletions

View File

@ -262,6 +262,41 @@ PGconn *PQsetdb(char *pghost,
</listitem>
</varlistentry>
<varlistentry id="libpq-PQsocketPoll">
<term><function>PQsocketPoll</function><indexterm><primary>PQsocketPoll</primary></indexterm></term>
<listitem>
<para>
<indexterm><primary>nonblocking connection</primary></indexterm>
Poll a connection&apos;s underlying socket descriptor retrieved with <xref linkend="libpq-PQsocket"/>.
<synopsis>
int PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time);
</synopsis>
</para>
<para>
This function sets up polling of a file descriptor. The underlying function is either
<function>poll(2)</function> or <function>select(2)</function>, depending on platform
support. The primary use of this function is iterating through the connection sequence
described in the documentation of <xref linkend="libpq-PQconnectStartParams"/>. If
<parameter>forRead</parameter> is specified, the function waits for the socket to be ready
for reading. If <parameter>forWrite</parameter> is specified, the function waits for the
socket to be ready for write. See <literal>POLLIN</literal> and <literal>POLLOUT</literal>
from <function>poll(2)</function>, or <parameter>readfds</parameter> and
<parameter>writefds</parameter> from <function>select(2)</function> for more information. If
<parameter>end_time</parameter> is not <literal>-1</literal>, it specifies the time at which
this function should stop waiting for the condition to be met.
</para>
<para>
The function returns a value greater than <literal>0</literal> if the specified condition
is met, <literal>0</literal> if a timeout occurred, or <literal>-1</literal> if an error
occurred. The error can be retrieved by checking the <literal>errno(3)</literal> value. In
the event <literal>forRead</literal> and <literal>forWrite</literal> are not set, the
function immediately returns a timeout condition.
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-PQconnectStartParams">
<term><function>PQconnectStartParams</function><indexterm><primary>PQconnectStartParams</primary></indexterm></term>
<term><function>PQconnectStart</function><indexterm><primary>PQconnectStart</primary></indexterm></term>
@ -358,7 +393,10 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
Loop thus: If <function>PQconnectPoll(conn)</function> last returned
<symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to
read (as indicated by <function>select()</function>, <function>poll()</function>, or
similar system function).
similar system function). Note that <function>PQsocketPoll</function>
can help reduce boilerplate by abstracting the setup of
<function>select(2)</function> or <function>poll(2)</function> if it is
available on your system.
Then call <function>PQconnectPoll(conn)</function> again.
Conversely, if <function>PQconnectPoll(conn)</function> last returned
<symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready

View File

@ -202,3 +202,4 @@ PQcancelSocket 199
PQcancelErrorMessage 200
PQcancelReset 201
PQcancelFinish 202
PQsocketPoll 203

View File

@ -55,7 +55,6 @@ static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
static int pqSendSome(PGconn *conn, int len);
static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
time_t end_time);
static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
/*
* PQlibVersion: return the libpq version number
@ -1059,7 +1058,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
/* We will retry as long as we get EINTR */
do
result = pqSocketPoll(conn->sock, forRead, forWrite, end_time);
result = PQsocketPoll(conn->sock, forRead, forWrite, end_time);
while (result < 0 && SOCK_ERRNO == EINTR);
if (result < 0)
@ -1083,8 +1082,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
* if end_time is 0 (or indeed, any time before now).
*/
static int
pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
int
PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time)
{
/* We use poll(2) if available, otherwise select(2) */
#ifdef HAVE_POLL

View File

@ -21,6 +21,7 @@ extern "C"
#endif
#include <stdio.h>
#include <time.h>
/*
* postgres_ext.h defines the backend's externally visible types,
@ -670,6 +671,9 @@ extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
/* Get the version of the libpq library in use */
extern int PQlibVersion(void);
/* Poll a socket for reading and/or writing with an optional timeout */
extern int PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time);
/* Determine length of multibyte encoded char at *s */
extern int PQmblen(const char *s, int encoding);