@ -28,8 +28,8 @@
*/
# ifdef WIN32
# define FD_SETSIZE 1024 /* set before winsock2.h is included */
# endif /* ! WIN32 */
# define FD_SETSIZE 1024 /* must set before winsock2.h is included */
# endif
# include "postgres_fe.h"
# include "fe_utils/conditional.h"
@ -45,12 +45,21 @@
# include <signal.h>
# include <time.h>
# include <sys/time.h>
# ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h> /* for getrlimit */
# endif
/* For testing, PGBENCH_USE_SELECT can be defined to force use of that code */
# if defined(HAVE_PPOLL) && !defined(PGBENCH_USE_SELECT)
# define POLL_USING_PPOLL
# ifdef HAVE_POLL_H
# include <poll.h>
# endif
# else /* no ppoll(), so use select() */
# define POLL_USING_SELECT
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
# ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h> /* for getrlimit */
# endif
# ifndef M_PI
@ -70,6 +79,33 @@
# define MM2_MUL_TIMES_8 UINT64CONST(0x35253c9ade8f4ca8)
# define MM2_ROT 47
/*
* Multi - platform socket set implementations
*/
# ifdef POLL_USING_PPOLL
# define SOCKET_WAIT_METHOD "ppoll"
typedef struct socket_set
{
int maxfds ; /* allocated length of pollfds[] array */
int curfds ; /* number currently in use */
struct pollfd pollfds [ FLEXIBLE_ARRAY_MEMBER ] ;
} socket_set ;
# endif /* POLL_USING_PPOLL */
# ifdef POLL_USING_SELECT
# define SOCKET_WAIT_METHOD "select"
typedef struct socket_set
{
int maxfd ; /* largest FD currently set in fds */
fd_set fds ;
} socket_set ;
# endif /* POLL_USING_SELECT */
/*
* Multi - platform pthread implementations
*/
@ -93,13 +129,6 @@ static int pthread_join(pthread_t th, void **thread_return);
/********************************************************************
* some configurable parameters */
/* max number of clients allowed */
# ifdef FD_SETSIZE
# define MAXCLIENTS (FD_SETSIZE - 10)
# else
# define MAXCLIENTS 1024
# endif
# define DEFAULT_INIT_STEPS "dtgvp" /* default -I setting */
# define LOG_STEP_SECONDS 5 /* seconds between log messages */
@ -523,8 +552,14 @@ static void processXactStats(TState *thread, CState *st, instr_time *now,
static void pgbench_error ( const char * fmt , . . . ) pg_attribute_printf ( 1 , 2 ) ;
static void addScript ( ParsedScript script ) ;
static void * threadRun ( void * arg ) ;
static void setalarm ( int seconds ) ;
static void finishCon ( CState * st ) ;
static void setalarm ( int seconds ) ;
static socket_set * alloc_socket_set ( int count ) ;
static void free_socket_set ( socket_set * sa ) ;
static void clear_socket_set ( socket_set * sa ) ;
static void add_socket_to_set ( socket_set * sa , int fd , int idx ) ;
static int wait_on_socket_set ( socket_set * sa , int64 usecs ) ;
static bool socket_has_input ( socket_set * sa , int fd , int idx ) ;
/* callback functions for our flex lexer */
@ -4903,7 +4938,7 @@ main(int argc, char **argv)
case ' c ' :
benchmarking_option_set = true ;
nclients = atoi ( optarg ) ;
if ( nclients < = 0 | | nclients > MAXCLIENTS )
if ( nclients < = 0 )
{
fprintf ( stderr , " invalid number of clients: \" %s \" \n " ,
optarg ) ;
@ -5606,6 +5641,7 @@ threadRun(void *arg)
end ;
int nstate = thread - > nstate ;
int remains = nstate ; /* number of remaining clients */
socket_set * sockets = alloc_socket_set ( nstate ) ;
int i ;
/* for reporting progress: */
@ -5673,14 +5709,16 @@ threadRun(void *arg)
/* loop till all clients have terminated */
while ( remains > 0 )
{
fd_set input_mask ;
int maxsock ; /* max socket number to be waited for */
int nsocks ; /* number of sockets to be waited for */
int64 min_usec ;
int64 now_usec = 0 ; /* set this only if needed */
/* identify which client sockets should be checked for input */
FD_ZERO ( & input_mask ) ;
maxsock = - 1 ;
/*
* identify which client sockets should be checked for input , and
* compute the nearest time ( if any ) at which we need to wake up .
*/
clear_socket_set ( sockets ) ;
nsocks = 0 ;
min_usec = PG_INT64_MAX ;
for ( i = 0 ; i < nstate ; i + + )
{
@ -5728,9 +5766,7 @@ threadRun(void *arg)
goto done ;
}
FD_SET ( sock , & input_mask ) ;
if ( maxsock < sock )
maxsock = sock ;
add_socket_to_set ( sockets , sock , nsocks + + ) ;
}
else if ( st - > state ! = CSTATE_ABORTED & &
st - > state ! = CSTATE_FINISHED )
@ -5764,35 +5800,29 @@ threadRun(void *arg)
/*
* If no clients are ready to execute actions , sleep until we receive
* data from the server , or a nap - time specified in the script ends ,
* or it ' s time to print a progress report . Update input_mask to show
* which client ( s ) received data .
* data on some client socket or the timeout ( if any ) elapses .
*/
if ( min_usec > 0 )
{
int nsocks = 0 ; /* return from select(2) if called */
int rc = 0 ;
if ( min_usec ! = PG_INT64_MAX )
{
if ( maxsock ! = - 1 )
if ( nsocks > 0 )
{
struct timeval timeout ;
timeout . tv_sec = min_usec / 1000000 ;
timeout . tv_usec = min_usec % 1000000 ;
nsocks = select ( maxsock + 1 , & input_mask , NULL , NULL , & timeout ) ;
rc = wait_on_socket_set ( sockets , min_usec ) ;
}
else /* nothing active, simple sleep */
{
pg_usleep ( min_usec ) ;
}
}
else /* no explicit delay, selec t without timeout */
else /* no explicit delay, wai t without timeout */
{
nsocks = select ( maxsock + 1 , & input_mask , NULL , NULL , NULL ) ;
rc = wait_on_socket_set ( sockets , 0 ) ;
}
if ( nsocks < 0 )
if ( rc < 0 )
{
if ( errno = = EINTR )
{
@ -5800,19 +5830,20 @@ threadRun(void *arg)
continue ;
}
/* must be something wrong */
fprintf ( stderr , " select () failed: %s \n " , strerror ( errno ) ) ;
fprintf ( stderr , " % s() failed: %s\n " , SOCKET_WAIT_METHOD , strerror ( errno ) ) ;
goto done ;
}
}
else
{
/* min_usec == 0, i.e. something needs to be executed */
/* min_usec <= 0, i.e. something needs to be executed now */
/* If we didn't call select() , don't try to read any data */
FD_ZERO ( & input_mask ) ;
/* If we didn't wait , don't try to read any data */
clear_socket_set ( sockets ) ;
}
/* ok, advance the state machine of each connection */
nsocks = 0 ;
for ( i = 0 ; i < nstate ; i + + )
{
CState * st = & state [ i ] ;
@ -5829,7 +5860,7 @@ threadRun(void *arg)
goto done ;
}
if ( ! FD_ISSET ( sock , & input_mask ) )
if ( ! socket_has_input ( sockets , sock , nsocks + + ) )
continue ;
}
else if ( st - > state = = CSTATE_FINISHED | |
@ -5967,6 +5998,7 @@ done:
fclose ( thread - > logfile ) ;
thread - > logfile = NULL ;
}
free_socket_set ( sockets ) ;
return NULL ;
}
@ -6025,8 +6057,185 @@ setalarm(int seconds)
}
}
# endif /* WIN32 */
/*
* These functions provide an abstraction layer that hides the syscall
* we use to wait for input on a set of sockets .
*
* Currently there are two implementations , based on ppoll ( 2 ) and select ( 2 ) .
* ppoll ( ) is preferred where available due to its typically higher ceiling
* on the number of usable sockets . We do not use the more - widely - available
* poll ( 2 ) because it only offers millisecond timeout resolution , which could
* be problematic with high - - rate settings .
*
* Function APIs :
*
* alloc_socket_set : allocate an empty socket set with room for up to
* " count " sockets .
*
* free_socket_set : deallocate a socket set .
*
* clear_socket_set : reset a socket set to empty .
*
* add_socket_to_set : add socket with indicated FD to slot " idx " in the
* socket set . Slots must be filled in order , starting with 0.
*
* wait_on_socket_set : wait for input on any socket in set , or for timeout
* to expire . timeout is measured in microseconds ; 0 means wait forever .
* Returns result code of underlying syscall ( > = 0 if OK , else see errno ) .
*
* socket_has_input : after waiting , call this to see if given socket has
* input . fd and idx parameters should match some previous call to
* add_socket_to_set .
*
* Note that wait_on_socket_set destructively modifies the state of the
* socket set . After checking for input , caller must apply clear_socket_set
* and add_socket_to_set again before waiting again .
*/
# ifdef POLL_USING_PPOLL
static socket_set *
alloc_socket_set ( int count )
{
socket_set * sa ;
sa = ( socket_set * ) pg_malloc0 ( offsetof ( socket_set , pollfds ) +
sizeof ( struct pollfd ) * count ) ;
sa - > maxfds = count ;
sa - > curfds = 0 ;
return sa ;
}
static void
free_socket_set ( socket_set * sa )
{
pg_free ( sa ) ;
}
static void
clear_socket_set ( socket_set * sa )
{
sa - > curfds = 0 ;
}
static void
add_socket_to_set ( socket_set * sa , int fd , int idx )
{
Assert ( idx < sa - > maxfds & & idx = = sa - > curfds ) ;
sa - > pollfds [ idx ] . fd = fd ;
sa - > pollfds [ idx ] . events = POLLIN ;
sa - > pollfds [ idx ] . revents = 0 ;
sa - > curfds + + ;
}
static int
wait_on_socket_set ( socket_set * sa , int64 usecs )
{
if ( usecs > 0 )
{
struct timespec timeout ;
timeout . tv_sec = usecs / 1000000 ;
timeout . tv_nsec = ( usecs % 1000000 ) * 1000 ;
return ppoll ( sa - > pollfds , sa - > curfds , & timeout , NULL ) ;
}
else
{
return ppoll ( sa - > pollfds , sa - > curfds , NULL , NULL ) ;
}
}
static bool
socket_has_input ( socket_set * sa , int fd , int idx )
{
/*
* In some cases , threadRun will apply clear_socket_set and then try to
* apply socket_has_input anyway with arguments that it used before that ,
* or might ' ve used before that except that it exited its setup loop
* early . Hence , if the socket set is empty , silently return false
* regardless of the parameters . If it ' s not empty , we can Assert that
* the parameters match a previous call .
*/
if ( sa - > curfds = = 0 )
return false ;
Assert ( idx < sa - > curfds & & sa - > pollfds [ idx ] . fd = = fd ) ;
return ( sa - > pollfds [ idx ] . revents & POLLIN ) ! = 0 ;
}
# endif /* POLL_USING_PPOLL */
# ifdef POLL_USING_SELECT
static socket_set *
alloc_socket_set ( int count )
{
return ( socket_set * ) pg_malloc0 ( sizeof ( socket_set ) ) ;
}
static void
free_socket_set ( socket_set * sa )
{
pg_free ( sa ) ;
}
static void
clear_socket_set ( socket_set * sa )
{
FD_ZERO ( & sa - > fds ) ;
sa - > maxfd = - 1 ;
}
static void
add_socket_to_set ( socket_set * sa , int fd , int idx )
{
if ( fd < 0 | | fd > = FD_SETSIZE )
{
/*
* Doing a hard exit here is a bit grotty , but it doesn ' t seem worth
* complicating the API to make it less grotty .
*/
fprintf ( stderr , " too many client connections for select() \n " ) ;
exit ( 1 ) ;
}
FD_SET ( fd , & sa - > fds ) ;
if ( fd > sa - > maxfd )
sa - > maxfd = fd ;
}
static int
wait_on_socket_set ( socket_set * sa , int64 usecs )
{
if ( usecs > 0 )
{
struct timeval timeout ;
timeout . tv_sec = usecs / 1000000 ;
timeout . tv_usec = usecs % 1000000 ;
return select ( sa - > maxfd + 1 , & sa - > fds , NULL , NULL , & timeout ) ;
}
else
{
return select ( sa - > maxfd + 1 , & sa - > fds , NULL , NULL , NULL ) ;
}
}
static bool
socket_has_input ( socket_set * sa , int fd , int idx )
{
return ( FD_ISSET ( fd , & sa - > fds ) ! = 0 ) ;
}
# endif /* POLL_USING_SELECT */
/* partial pthread implementation for Windows */
# ifdef WIN32
typedef struct win32_pthread
{
HANDLE handle ;