Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit105024a

Browse files
committed
Improve the granularity of PQsocketPoll's timeout parameter.
Commitf5e4ded exposed libpq's internal function PQsocketPollwithout a lot of thought about whether that was an API we reallywanted to chisel in stone. The main problem with it is the use oftime_t to specify the timeout. While we do want an absolute timeso that a loop around PQsocketPoll doesn't have problems withtimeout slippage, time_t has only 1-second resolution. That'salready problematic for libpq's own internal usage --- for example,pqConnectDBComplete has long had a kluge to treat "connect_timeout=1"as 2 seconds so that it doesn't accidentally round to nearly zero.And it's even less likely to be satisfactory for external callers.Hence, let's change this while we still can.The best idea seems to be to use an int64 count of microseconds sincethe epoch --- basically the same thing as the backend's TimestampTz,but let's use the standard Unix epoch (1970-01-01) since that's morelikely for clients to be easy to calculate. Millisecond resolutionwould be plenty for foreseeable uses, but maybe the day will come thatwe're glad we used microseconds.Also, since time(2) isn't especially helpful for computing timeoutsdefined this way, introduce a new function PQgetCurrentTimeUSecto get the current time in this form.Remove the hack in pqConnectDBComplete, so that "connect_timeout=1"now means what you'd expect.We can also remove the "#include <time.h>" thatf5e4ded added tolibpq-fe.h, since there's no longer a need for time_t in that header.It seems better for v17 not to enlarge libpq-fe.h's include footprintfrom what it's historically been, anyway.I also failed to resist the temptation to do some wordsmithingon PQsocketPoll's documentation.Patch by me, per complaint from Dominique Devienne.Discussion:https://postgr.es/m/913559.1718055575@sss.pgh.pa.us
1 parent6dfac24 commit105024a

File tree

8 files changed

+153
-78
lines changed

8 files changed

+153
-78
lines changed

‎doc/src/sgml/libpq.sgml

Lines changed: 84 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -262,41 +262,6 @@ PGconn *PQsetdb(char *pghost,
262262
</listitem>
263263
</varlistentry>
264264

265-
<varlistentry id="libpq-PQsocketPoll">
266-
<term><function>PQsocketPoll</function><indexterm><primary>PQsocketPoll</primary></indexterm></term>
267-
<listitem>
268-
<para>
269-
<indexterm><primary>nonblocking connection</primary></indexterm>
270-
Poll a connection&apos;s underlying socket descriptor retrieved with <xref linkend="libpq-PQsocket"/>.
271-
<synopsis>
272-
int PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time);
273-
</synopsis>
274-
</para>
275-
276-
<para>
277-
This function sets up polling of a file descriptor. The underlying function is either
278-
<function>poll(2)</function> or <function>select(2)</function>, depending on platform
279-
support. The primary use of this function is iterating through the connection sequence
280-
described in the documentation of <xref linkend="libpq-PQconnectStartParams"/>. If
281-
<parameter>forRead</parameter> is specified, the function waits for the socket to be ready
282-
for reading. If <parameter>forWrite</parameter> is specified, the function waits for the
283-
socket to be ready for write. See <literal>POLLIN</literal> and <literal>POLLOUT</literal>
284-
from <function>poll(2)</function>, or <parameter>readfds</parameter> and
285-
<parameter>writefds</parameter> from <function>select(2)</function> for more information. If
286-
<parameter>end_time</parameter> is not <literal>-1</literal>, it specifies the time at which
287-
this function should stop waiting for the condition to be met.
288-
</para>
289-
290-
<para>
291-
The function returns a value greater than <literal>0</literal> if the specified condition
292-
is met, <literal>0</literal> if a timeout occurred, or <literal>-1</literal> if an error
293-
occurred. The error can be retrieved by checking the <literal>errno(3)</literal> value. In
294-
the event <literal>forRead</literal> and <literal>forWrite</literal> are not set, the
295-
function immediately returns a timeout condition.
296-
</para>
297-
</listitem>
298-
</varlistentry>
299-
300265
<varlistentry id="libpq-PQconnectStartParams">
301266
<term><function>PQconnectStartParams</function><indexterm><primary>PQconnectStartParams</primary></indexterm></term>
302267
<term><function>PQconnectStart</function><indexterm><primary>PQconnectStart</primary></indexterm></term>
@@ -546,6 +511,70 @@ switch(PQstatus(conn))
546511
</listitem>
547512
</varlistentry>
548513

514+
<varlistentry id="libpq-PQsocketPoll">
515+
<term><function>PQsocketPoll</function><indexterm><primary>PQsocketPoll</primary></indexterm></term>
516+
<listitem>
517+
<para>
518+
<indexterm><primary>nonblocking connection</primary></indexterm>
519+
Poll a connection's underlying socket descriptor retrieved with
520+
<xref linkend="libpq-PQsocket"/>.
521+
The primary use of this function is iterating through the connection
522+
sequence described in the documentation of
523+
<xref linkend="libpq-PQconnectStartParams"/>.
524+
<synopsis>
525+
typedef pg_int64 pg_usec_time_t;
526+
527+
int PQsocketPoll(int sock, int forRead, int forWrite,
528+
pg_usec_time_t end_time);
529+
</synopsis>
530+
</para>
531+
532+
<para>
533+
This function performs polling of a file descriptor, optionally with
534+
a timeout.
535+
If <parameter>forRead</parameter> is nonzero, the
536+
function will terminate when the socket is ready for
537+
reading. If <parameter>forWrite</parameter> is nonzero,
538+
the function will terminate when the
539+
socket is ready for writing.
540+
</para>
541+
542+
<para>
543+
The timeout is specified by <parameter>end_time</parameter>, which
544+
is the time to stop waiting expressed as a number of microseconds since
545+
the Unix epoch (that is, <type>time_t</type> times 1 million).
546+
Timeout is infinite if <parameter>end_time</parameter>
547+
is <literal>-1</literal>. Timeout is immediate (no blocking) if
548+
end_time is <literal>0</literal> (or indeed, any time before now).
549+
Timeout values can be calculated conveniently by adding the desired
550+
number of microseconds to the result of
551+
<xref linkend="libpq-PQgetCurrentTimeUSec"/>.
552+
Note that the underlying system calls may have less than microsecond
553+
precision, so that the actual delay may be imprecise.
554+
</para>
555+
556+
<para>
557+
The function returns a value greater than <literal>0</literal> if the
558+
specified condition is met, <literal>0</literal> if a timeout occurred,
559+
or <literal>-1</literal> if an error occurred. The error can be
560+
retrieved by checking the <literal>errno(3)</literal> value. In the
561+
event both <parameter>forRead</parameter>
562+
and <parameter>forWrite</parameter> are zero, the function immediately
563+
returns a timeout indication.
564+
</para>
565+
566+
<para>
567+
<function>PQsocketPoll</function> is implemented using either
568+
<function>poll(2)</function> or <function>select(2)</function>,
569+
depending on platform. See <literal>POLLIN</literal>
570+
and <literal>POLLOUT</literal> from <function>poll(2)</function>,
571+
or <parameter>readfds</parameter> and
572+
<parameter>writefds</parameter> from <function>select(2)</function>,
573+
for more information.
574+
</para>
575+
</listitem>
576+
</varlistentry>
577+
549578
<varlistentry id="libpq-PQconndefaults">
550579
<term><function>PQconndefaults</function><indexterm><primary>PQconndefaults</primary></indexterm></term>
551580
<listitem>
@@ -1390,8 +1419,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
13901419
<para>
13911420
Maximum time to wait while connecting, in seconds (write as a decimal integer,
13921421
e.g., <literal>10</literal>). Zero, negative, or not specified means
1393-
wait indefinitely. The minimum allowed timeout is 2 seconds, therefore
1394-
a value of <literal>1</literal> is interpreted as <literal>2</literal>.
1422+
wait indefinitely.
13951423
This timeout applies separately to each host name or IP address.
13961424
For example, if you specify two hosts and <literal>connect_timeout</literal>
13971425
is 5, each host will time out if no connection is made within 5
@@ -8039,6 +8067,25 @@ int PQlibVersion(void);
80398067
</listitem>
80408068
</varlistentry>
80418069

8070+
<varlistentry id="libpq-PQgetCurrentTimeUSec">
8071+
<term><function>PQgetCurrentTimeUSec</function><indexterm><primary>PQgetCurrentTimeUSec</primary></indexterm></term>
8072+
8073+
<listitem>
8074+
<para>
8075+
Retrieves the current time, expressed as the number of microseconds
8076+
since the Unix epoch (that is, <type>time_t</type> times 1 million).
8077+
<synopsis>
8078+
pg_usec_time_t PQgetCurrentTimeUSec(void);
8079+
</synopsis>
8080+
</para>
8081+
8082+
<para>
8083+
This is primarily useful for calculating timeout values to use with
8084+
<xref linkend="libpq-PQsocketPoll"/>.
8085+
</para>
8086+
</listitem>
8087+
</varlistentry>
8088+
80428089
</variablelist>
80438090

80448091
</sect1>

‎src/bin/psql/command.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3764,7 +3764,7 @@ wait_until_connected(PGconn *conn)
37643764
{
37653765
intrc;
37663766
intsock;
3767-
time_tend_time;
3767+
pg_usec_time_tend_time;
37683768

37693769
/*
37703770
* On every iteration of the connection sequence, let's check if the
@@ -3795,7 +3795,7 @@ wait_until_connected(PGconn *conn)
37953795
* solution happens to just be adding a timeout, so let's wait for 1
37963796
* second and check cancel_pressed again.
37973797
*/
3798-
end_time=time(NULL)+1;
3798+
end_time=PQgetCurrentTimeUSec()+1000000;
37993799
rc=PQsocketPoll(sock,forRead, !forRead,end_time);
38003800
if (rc==-1)
38013801
return;

‎src/interfaces/libpq/exports.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,4 @@ PQcancelReset 201
204204
PQcancelFinish 202
205205
PQsocketPoll 203
206206
PQsetChunkedRowsMode 204
207+
PQgetCurrentTimeUSec 205

‎src/interfaces/libpq/fe-connect.c

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,7 +2470,7 @@ int
24702470
pqConnectDBComplete(PGconn*conn)
24712471
{
24722472
PostgresPollingStatusTypeflag=PGRES_POLLING_WRITING;
2473-
time_tfinish_time= ((time_t)-1);
2473+
pg_usec_time_tend_time=-1;
24742474
inttimeout=0;
24752475
intlast_whichhost=-2;/* certainly different from whichhost */
24762476
intlast_whichaddr=-2;/* certainly different from whichaddr */
@@ -2479,7 +2479,7 @@ pqConnectDBComplete(PGconn *conn)
24792479
return0;
24802480

24812481
/*
2482-
* Set up a time limit, if connect_timeoutisn't zero.
2482+
* Set up a time limit, if connect_timeoutis greater than zero.
24832483
*/
24842484
if (conn->connect_timeout!=NULL)
24852485
{
@@ -2490,19 +2490,6 @@ pqConnectDBComplete(PGconn *conn)
24902490
conn->status=CONNECTION_BAD;
24912491
return0;
24922492
}
2493-
2494-
if (timeout>0)
2495-
{
2496-
/*
2497-
* Rounding could cause connection to fail unexpectedly quickly;
2498-
* to prevent possibly waiting hardly-at-all, insist on at least
2499-
* two seconds.
2500-
*/
2501-
if (timeout<2)
2502-
timeout=2;
2503-
}
2504-
else/* negative means 0 */
2505-
timeout=0;
25062493
}
25072494

25082495
for (;;)
@@ -2519,7 +2506,7 @@ pqConnectDBComplete(PGconn *conn)
25192506
(conn->whichhost!=last_whichhost||
25202507
conn->whichaddr!=last_whichaddr))
25212508
{
2522-
finish_time=time(NULL)+timeout;
2509+
end_time=PQgetCurrentTimeUSec()+(pg_usec_time_t)timeout*1000000;
25232510
last_whichhost=conn->whichhost;
25242511
last_whichaddr=conn->whichaddr;
25252512
}
@@ -2534,7 +2521,7 @@ pqConnectDBComplete(PGconn *conn)
25342521
return1;/* success! */
25352522

25362523
casePGRES_POLLING_READING:
2537-
ret=pqWaitTimed(1,0,conn,finish_time);
2524+
ret=pqWaitTimed(1,0,conn,end_time);
25382525
if (ret==-1)
25392526
{
25402527
/* hard failure, eg select() problem, aborts everything */
@@ -2544,7 +2531,7 @@ pqConnectDBComplete(PGconn *conn)
25442531
break;
25452532

25462533
casePGRES_POLLING_WRITING:
2547-
ret=pqWaitTimed(0,1,conn,finish_time);
2534+
ret=pqWaitTimed(0,1,conn,end_time);
25482535
if (ret==-1)
25492536
{
25502537
/* hard failure, eg select() problem, aborts everything */

‎src/interfaces/libpq/fe-misc.c

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
staticintpqPutMsgBytes(constvoid*buf,size_tlen,PGconn*conn);
5555
staticintpqSendSome(PGconn*conn,intlen);
5656
staticintpqSocketCheck(PGconn*conn,intforRead,intforWrite,
57-
time_tend_time);
57+
pg_usec_time_tend_time);
5858

5959
/*
6060
* PQlibVersion: return the libpq version number
@@ -977,22 +977,25 @@ pqFlush(PGconn *conn)
977977
int
978978
pqWait(intforRead,intforWrite,PGconn*conn)
979979
{
980-
returnpqWaitTimed(forRead,forWrite,conn,(time_t)-1);
980+
returnpqWaitTimed(forRead,forWrite,conn,-1);
981981
}
982982

983983
/*
984-
* pqWaitTimed: wait, but not past finish_time.
985-
*
986-
* finish_time = ((time_t) -1) disables the wait limit.
984+
* pqWaitTimed: wait, but not past end_time.
987985
*
988986
* Returns -1 on failure, 0 if the socket is readable/writable, 1 if it timed out.
987+
*
988+
* The timeout is specified by end_time, which is the int64 number of
989+
* microseconds since the Unix epoch (that is, time_t times 1 million).
990+
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
991+
* if end_time is 0 (or indeed, any time before now).
989992
*/
990993
int
991-
pqWaitTimed(intforRead,intforWrite,PGconn*conn,time_tfinish_time)
994+
pqWaitTimed(intforRead,intforWrite,PGconn*conn,pg_usec_time_tend_time)
992995
{
993996
intresult;
994997

995-
result=pqSocketCheck(conn,forRead,forWrite,finish_time);
998+
result=pqSocketCheck(conn,forRead,forWrite,end_time);
996999

9971000
if (result<0)
9981001
return-1;/* errorMessage is already set */
@@ -1013,7 +1016,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
10131016
int
10141017
pqReadReady(PGconn*conn)
10151018
{
1016-
returnpqSocketCheck(conn,1,0,(time_t)0);
1019+
returnpqSocketCheck(conn,1,0,0);
10171020
}
10181021

10191022
/*
@@ -1023,7 +1026,7 @@ pqReadReady(PGconn *conn)
10231026
int
10241027
pqWriteReady(PGconn*conn)
10251028
{
1026-
returnpqSocketCheck(conn,0,1,(time_t)0);
1029+
returnpqSocketCheck(conn,0,1,0);
10271030
}
10281031

10291032
/*
@@ -1035,7 +1038,7 @@ pqWriteReady(PGconn *conn)
10351038
* for read data directly.
10361039
*/
10371040
staticint
1038-
pqSocketCheck(PGconn*conn,intforRead,intforWrite,time_tend_time)
1041+
pqSocketCheck(PGconn*conn,intforRead,intforWrite,pg_usec_time_tend_time)
10391042
{
10401043
intresult;
10411044

@@ -1079,11 +1082,13 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
10791082
* condition (without waiting). Return >0 if condition is met, 0
10801083
* if a timeout occurred, -1 if an error or interrupt occurred.
10811084
*
1085+
* The timeout is specified by end_time, which is the int64 number of
1086+
* microseconds since the Unix epoch (that is, time_t times 1 million).
10821087
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
10831088
* if end_time is 0 (or indeed, any time before now).
10841089
*/
10851090
int
1086-
PQsocketPoll(intsock,intforRead,intforWrite,time_tend_time)
1091+
PQsocketPoll(intsock,intforRead,intforWrite,pg_usec_time_tend_time)
10871092
{
10881093
/* We use poll(2) if available, otherwise select(2) */
10891094
#ifdefHAVE_POLL
@@ -1103,14 +1108,16 @@ PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time)
11031108
input_fd.events |=POLLOUT;
11041109

11051110
/* Compute appropriate timeout interval */
1106-
if (end_time==((time_t)-1))
1111+
if (end_time==-1)
11071112
timeout_ms=-1;
1113+
elseif (end_time==0)
1114+
timeout_ms=0;
11081115
else
11091116
{
1110-
time_tnow=time(NULL);
1117+
pg_usec_time_tnow=PQgetCurrentTimeUSec();
11111118

11121119
if (end_time>now)
1113-
timeout_ms= (end_time-now)*1000;
1120+
timeout_ms= (end_time-now)/1000;
11141121
else
11151122
timeout_ms=0;
11161123
}
@@ -1138,17 +1145,28 @@ PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time)
11381145
FD_SET(sock,&except_mask);
11391146

11401147
/* Compute appropriate timeout interval */
1141-
if (end_time==((time_t)-1))
1148+
if (end_time==-1)
11421149
ptr_timeout=NULL;
1150+
elseif (end_time==0)
1151+
{
1152+
timeout.tv_sec=0;
1153+
timeout.tv_usec=0;
1154+
ptr_timeout=&timeout;
1155+
}
11431156
else
11441157
{
1145-
time_tnow=time(NULL);
1158+
pg_usec_time_tnow=PQgetCurrentTimeUSec();
11461159

11471160
if (end_time>now)
1148-
timeout.tv_sec=end_time-now;
1161+
{
1162+
timeout.tv_sec= (end_time-now) /1000000;
1163+
timeout.tv_usec= (end_time-now) %1000000;
1164+
}
11491165
else
1166+
{
11501167
timeout.tv_sec=0;
1151-
timeout.tv_usec=0;
1168+
timeout.tv_usec=0;
1169+
}
11521170
ptr_timeout=&timeout;
11531171
}
11541172

@@ -1157,6 +1175,21 @@ PQsocketPoll(int sock, int forRead, int forWrite, time_t end_time)
11571175
#endif/* HAVE_POLL */
11581176
}
11591177

1178+
/*
1179+
* PQgetCurrentTimeUSec: get current time with microsecond precision
1180+
*
1181+
* This provides a platform-independent way of producing a reference
1182+
* value for PQsocketPoll's timeout parameter.
1183+
*/
1184+
pg_usec_time_t
1185+
PQgetCurrentTimeUSec(void)
1186+
{
1187+
structtimevaltval;
1188+
1189+
gettimeofday(&tval,NULL);
1190+
return (pg_usec_time_t)tval.tv_sec*1000000+tval.tv_usec;
1191+
}
1192+
11601193

11611194
/*
11621195
* A couple of "miscellaneous" multibyte related functions. They used

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp