diff options
Diffstat (limited to 'src/backend/storage/ipc/signalfuncs.c')
-rw-r--r-- | src/backend/storage/ipc/signalfuncs.c | 124 |
1 files changed, 120 insertions, 4 deletions
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c index 8b55ff6e76b..0337b00226a 100644 --- a/src/backend/storage/ipc/signalfuncs.c +++ b/src/backend/storage/ipc/signalfuncs.c @@ -18,6 +18,7 @@ #include "catalog/pg_authid.h" #include "miscadmin.h" +#include "pgstat.h" #include "postmaster/syslogger.h" #include "storage/pmsignal.h" #include "storage/proc.h" @@ -126,15 +127,90 @@ pg_cancel_backend(PG_FUNCTION_ARGS) } /* - * Signal to terminate a backend process. This is allowed if you are a member - * of the role whose process is being terminated. + * Wait until there is no backend process with the given PID and return true. + * On timeout, a warning is emitted and false is returned. + */ +static bool +pg_wait_until_termination(int pid, int64 timeout) +{ + /* + * Wait in steps of waittime milliseconds until this function exits or + * timeout. + */ + int64 waittime = 100; + /* + * Initially remaining time is the entire timeout specified by the user. + */ + int64 remainingtime = timeout; + + /* + * Check existence of the backend. If the backend still exists, then wait + * for waittime milliseconds, again check for the existence. Repeat this + * until timeout or an error occurs or a pending interrupt such as query + * cancel gets processed. + */ + do + { + if (remainingtime < waittime) + waittime = remainingtime; + + if (kill(pid, 0) == -1) + { + if (errno == ESRCH) + return true; + else + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not check the existence of the backend with PID %d: %m", + pid))); + } + + /* Process interrupts, if any, before waiting */ + CHECK_FOR_INTERRUPTS(); + + (void) WaitLatch(MyLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, + waittime, + WAIT_EVENT_BACKEND_TERMINATION); + + ResetLatch(MyLatch); + + remainingtime -= waittime; + } while (remainingtime > 0); + + ereport(WARNING, + (errmsg("backend with PID %d did not terminate within %lld milliseconds", + pid, (long long int) timeout))); + + return false; +} + +/* + * Signal to terminate a backend process. This is allowed if you are a member + * of the role whose process is being terminated. If timeout input argument is + * 0 (which is default), then this function just signals the backend and + * doesn't wait. Otherwise it waits until given the timeout milliseconds or no + * process has the given PID and returns true. On timeout, a warning is emitted + * and false is returned. * * Note that only superusers can signal superuser-owned processes. */ Datum pg_terminate_backend(PG_FUNCTION_ARGS) { - int r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM); + int pid; + int r; + int timeout; + + pid = PG_GETARG_INT32(0); + timeout = PG_GETARG_INT64(1); + + if (timeout < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"timeout\" must not be negative"))); + + r = pg_signal_backend(pid, SIGTERM); if (r == SIGNAL_BACKEND_NOSUPERUSER) ereport(ERROR, @@ -146,7 +222,47 @@ pg_terminate_backend(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))); - PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS); + /* Wait only on success and if actually requested */ + if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0) + PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout)); + else + PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS); +} + +/* + * Wait for a backend process with the given PID to exit or until the given + * timeout milliseconds occurs. Returns true if the backend has exited. On + * timeout a warning is emitted and false is returned. + * + * We allow any user to call this function, consistent with any user being + * able to view the pid of the process in pg_stat_activity etc. + */ +Datum +pg_wait_for_backend_termination(PG_FUNCTION_ARGS) +{ + int pid; + int64 timeout; + PGPROC *proc = NULL; + + pid = PG_GETARG_INT32(0); + timeout = PG_GETARG_INT64(1); + + if (timeout <= 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"timeout\" must not be negative or zero"))); + + proc = BackendPidGetProc(pid); + + if (proc == NULL) + { + ereport(WARNING, + (errmsg("PID %d is not a PostgreSQL server process", pid))); + + PG_RETURN_BOOL(false); + } + + PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout)); } /* |