18
18
19
19
#include "catalog/pg_authid.h"
20
20
#include "miscadmin.h"
21
+ #include "pgstat.h"
21
22
#include "postmaster/syslogger.h"
22
23
#include "storage/pmsignal.h"
23
24
#include "storage/proc.h"
@@ -126,15 +127,90 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
126
127
}
127
128
128
129
/*
129
- * Signal to terminate a backend process. This is allowed if you are a member
130
- * of the role whose process is being terminated.
130
+ * Wait until there is no backend process with the given PID and return true.
131
+ * On timeout, a warning is emitted and false is returned.
132
+ */
133
+ static bool
134
+ pg_wait_until_termination (int pid , int64 timeout )
135
+ {
136
+ /*
137
+ * Wait in steps of waittime milliseconds until this function exits or
138
+ * timeout.
139
+ */
140
+ int64 waittime = 100 ;
141
+ /*
142
+ * Initially remaining time is the entire timeout specified by the user.
143
+ */
144
+ int64 remainingtime = timeout ;
145
+
146
+ /*
147
+ * Check existence of the backend. If the backend still exists, then wait
148
+ * for waittime milliseconds, again check for the existence. Repeat this
149
+ * until timeout or an error occurs or a pending interrupt such as query
150
+ * cancel gets processed.
151
+ */
152
+ do
153
+ {
154
+ if (remainingtime < waittime )
155
+ waittime = remainingtime ;
156
+
157
+ if (kill (pid , 0 ) == -1 )
158
+ {
159
+ if (errno == ESRCH )
160
+ return true;
161
+ else
162
+ ereport (ERROR ,
163
+ (errcode (ERRCODE_INTERNAL_ERROR ),
164
+ errmsg ("could not check the existence of the backend with PID %d: %m" ,
165
+ pid )));
166
+ }
167
+
168
+ /* Process interrupts, if any, before waiting */
169
+ CHECK_FOR_INTERRUPTS ();
170
+
171
+ (void ) WaitLatch (MyLatch ,
172
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH ,
173
+ waittime ,
174
+ WAIT_EVENT_BACKEND_TERMINATION );
175
+
176
+ ResetLatch (MyLatch );
177
+
178
+ remainingtime -= waittime ;
179
+ } while (remainingtime > 0 );
180
+
181
+ ereport (WARNING ,
182
+ (errmsg ("backend with PID %d did not terminate within %lld milliseconds" ,
183
+ pid , (long long int ) timeout )));
184
+
185
+ return false;
186
+ }
187
+
188
+ /*
189
+ * Signal to terminate a backend process. This is allowed if you are a member
190
+ * of the role whose process is being terminated. If timeout input argument is
191
+ * 0 (which is default), then this function just signals the backend and
192
+ * doesn't wait. Otherwise it waits until given the timeout milliseconds or no
193
+ * process has the given PID and returns true. On timeout, a warning is emitted
194
+ * and false is returned.
131
195
*
132
196
* Note that only superusers can signal superuser-owned processes.
133
197
*/
134
198
Datum
135
199
pg_terminate_backend (PG_FUNCTION_ARGS )
136
200
{
137
- int r = pg_signal_backend (PG_GETARG_INT32 (0 ), SIGTERM );
201
+ int pid ;
202
+ int r ;
203
+ int timeout ;
204
+
205
+ pid = PG_GETARG_INT32 (0 );
206
+ timeout = PG_GETARG_INT64 (1 );
207
+
208
+ if (timeout < 0 )
209
+ ereport (ERROR ,
210
+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
211
+ errmsg ("\"timeout\" must not be negative" )));
212
+
213
+ r = pg_signal_backend (pid , SIGTERM );
138
214
139
215
if (r == SIGNAL_BACKEND_NOSUPERUSER )
140
216
ereport (ERROR ,
@@ -146,7 +222,47 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
146
222
(errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
147
223
errmsg ("must be a member of the role whose process is being terminated or member of pg_signal_backend" )));
148
224
149
- PG_RETURN_BOOL (r == SIGNAL_BACKEND_SUCCESS );
225
+ /* Wait only on success and if actually requested */
226
+ if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0 )
227
+ PG_RETURN_BOOL (pg_wait_until_termination (pid , timeout ));
228
+ else
229
+ PG_RETURN_BOOL (r == SIGNAL_BACKEND_SUCCESS );
230
+ }
231
+
232
+ /*
233
+ * Wait for a backend process with the given PID to exit or until the given
234
+ * timeout milliseconds occurs. Returns true if the backend has exited. On
235
+ * timeout a warning is emitted and false is returned.
236
+ *
237
+ * We allow any user to call this function, consistent with any user being
238
+ * able to view the pid of the process in pg_stat_activity etc.
239
+ */
240
+ Datum
241
+ pg_wait_for_backend_termination (PG_FUNCTION_ARGS )
242
+ {
243
+ int pid ;
244
+ int64 timeout ;
245
+ PGPROC * proc = NULL ;
246
+
247
+ pid = PG_GETARG_INT32 (0 );
248
+ timeout = PG_GETARG_INT64 (1 );
249
+
250
+ if (timeout <= 0 )
251
+ ereport (ERROR ,
252
+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
253
+ errmsg ("\"timeout\" must not be negative or zero" )));
254
+
255
+ proc = BackendPidGetProc (pid );
256
+
257
+ if (proc == NULL )
258
+ {
259
+ ereport (WARNING ,
260
+ (errmsg ("PID %d is not a PostgreSQL server process" , pid )));
261
+
262
+ PG_RETURN_BOOL (false);
263
+ }
264
+
265
+ PG_RETURN_BOOL (pg_wait_until_termination (pid , timeout ));
150
266
}
151
267
152
268
/*
0 commit comments