From bf2d2fed7419ba9570f9e8db00da642cd087761d Mon Sep 17 00:00:00 2001 From: roman khapov Date: Mon, 26 Oct 2026 10:54:21 +0500 Subject: [PATCH] pgbench: print partial results on SIGINT Previously, Ctrl-C killed pgbench outright, so none of the statistics collected up to that point were shown. Instead, handle SIGINT gracefully: stop accepting new transactions, discard in-flight ones, aggregate whatever has already been measured and print the summary as usual, exiting with status 130. A second Ctrl-C continues to kill the process immediately. Waits are capped at one second (MAX_IDLE_WAIT_USEC) so that the interruption is noticed promptly even when all clients of a thread are idle in a \sleep with no sockets to be woken on, as pg_usleep() does not return early on EINTR. Signed-off-by: roman khapov --- src/bin/pgbench/pgbench.c | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 181f0d892ef..5a570ba79ec 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -304,12 +304,14 @@ static const char *progname; static volatile sig_atomic_t timer_exceeded = false; /* flag from signal * handler */ +static volatile sig_atomic_t aborted_by_sigint = false; /* Ctrl-C pressed */ /* * We don't want to allocate variables one by one; for efficiency, add a * constant margin each time it overflows. */ #define VARIABLES_ALLOC_MARGIN 8 +#define MAX_IDLE_WAIT_USEC INT64CONST(1000000) /* * Variable definitions. @@ -842,6 +844,7 @@ static void addScript(const ParsedScript *script); static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC threadRun(void *arg); static void finishCon(CState *st); static void setalarm(int seconds); +static void install_sigint_handler(void); static socket_set *alloc_socket_set(int count); static void free_socket_set(socket_set *sa); static void clear_socket_set(socket_set *sa); @@ -7452,6 +7455,12 @@ main(int argc, char **argv) if (duration > 0) setalarm(duration); + /* + * Install a SIGINT handler so that Ctrl-C stops the benchmark and + * reports the partial results gathered so far. + */ + install_sigint_handler(); + errno = THREAD_BARRIER_INIT(&barrier, nthreads); if (errno != 0) pg_fatal("could not initialize barrier: %m"); @@ -7528,6 +7537,9 @@ main(int argc, char **argv) THREAD_BARRIER_DESTROY(&barrier); + if (aborted_by_sigint) + exit_code = 130; + if (exit_code != 0) pg_log_error("Run was aborted; the above results are incomplete."); @@ -7618,6 +7630,17 @@ threadRun(void *arg) pg_time_usec_t min_usec; pg_time_usec_t now = 0; /* set this only if needed */ + if (aborted_by_sigint) + { + for (int i = 0; i < nstate; i++) + { + if (state[i].state != CSTATE_FINISHED && + state[i].state != CSTATE_ABORTED) + state[i].state = CSTATE_FINISHED; + } + break; + } + /* * identify which client sockets should be checked for input, and * compute the nearest time (if any) at which we need to wake up. @@ -7686,7 +7709,12 @@ threadRun(void *arg) /* * If no clients are ready to execute actions, sleep until we receive * data on some client socket or the timeout (if any) elapses. + * + * pg_usleep() wait for all specified time and ignores EINTR, + * so lets do a little compromise: do sleeping with little intervals */ + if (min_usec > MAX_IDLE_WAIT_USEC) + min_usec = MAX_IDLE_WAIT_USEC; if (min_usec > 0) { int rc = 0; @@ -7863,6 +7891,23 @@ setalarm(int seconds) alarm(seconds); } +static void +handle_sig_int(SIGNAL_ARGS) +{ + if (aborted_by_sigint) + { + pqsignal(SIGINT, PG_SIG_DFL); + raise(SIGINT); + } + aborted_by_sigint = true; +} + +static void +install_sigint_handler(void) +{ + pqsignal(SIGINT, handle_sig_int); +} + #else /* WIN32 */ static VOID CALLBACK @@ -7886,6 +7931,25 @@ setalarm(int seconds) pg_fatal("failed to set timer"); } +static BOOL WINAPI +win32_sigint_callback(DWORD ctrl_type) +{ + if (ctrl_type == CTRL_C_EVENT || ctrl_type == CTRL_BREAK_EVENT) + { + if (aborted_by_sigint) + return FALSE; + aborted_by_sigint = true; + return TRUE; + } + return FALSE; +} + +static void +install_sigint_handler(void) +{ + SetConsoleCtrlHandler(win32_sigint_callback, TRUE); +} + #endif /* WIN32 */ -- 2.50.1 (Apple Git-155)