Skip to content

Commit 4796880

Browse files
author
Sergio Conde Gomez
committed
Make execution of plpython interruptible
The plpy module hooks into the signal handling mechanism to insert python exceptions when a SIGINT occurs.
1 parent 958fe54 commit 4796880

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

Diff for: src/pl/plpython/plpy_main.c

+50
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,19 @@ PyObject *PLy_interp_globals = NULL;
7373
/* this doesn't need to be global; use PLy_current_execution_context() */
7474
static PLyExecutionContext *PLy_execution_contexts = NULL;
7575

76+
/* postgres backend handler for interruption */
77+
static pqsigfunc coreIntHandler = 0;
78+
static void PLy_handle_interrupt(int sig);
79+
7680

7781
void
7882
_PG_init(void)
7983
{
8084
int **bitmask_ptr;
8185

86+
/* Catch and process SIGINT signals */
87+
coreIntHandler = pqsignal(SIGINT, PLy_handle_interrupt);
88+
8289
/*
8390
* Set up a shared bitmask variable telling which Python version(s) are
8491
* loaded into this process's address space. If there's more than one, we
@@ -418,6 +425,9 @@ PLy_get_scratch_context(PLyExecutionContext *context)
418425
return context->scratch_ctx;
419426
}
420427

428+
/* Indicate tha a python interruption is pending */
429+
static int PLy_pending_interrupt = 0;
430+
421431
static PLyExecutionContext *
422432
PLy_push_execution_context(void)
423433
{
@@ -442,7 +452,47 @@ PLy_pop_execution_context(void)
442452

443453
PLy_execution_contexts = context->next;
444454

455+
if (PLy_execution_contexts == NULL) {
456+
// Clear pending interrupts when top level context exits
457+
PLy_pending_interrupt = 0;
458+
}
459+
445460
if (context->scratch_ctx)
446461
MemoryContextDelete(context->scratch_ctx);
447462
pfree(context);
448463
}
464+
465+
void
466+
_PG_fini(void)
467+
{
468+
// Restore previous SIGINT handler
469+
pqsignal(SIGINT, coreIntHandler);
470+
}
471+
472+
static int
473+
PLy_python_interruption_handler()
474+
{
475+
if (!PLy_pending_interrupt) {
476+
return 0;
477+
}
478+
479+
PLy_pending_interrupt = 0;
480+
PyErr_SetString(PyExc_RuntimeError, "Execution of function interrupted by signal");
481+
return -1;
482+
}
483+
484+
static void
485+
PLy_handle_interrupt(int sig)
486+
{
487+
if (PLy_execution_contexts != NULL && !PLy_pending_interrupt) {
488+
PLy_pending_interrupt = 1;
489+
Py_AddPendingCall(PLy_python_interruption_handler, NULL);
490+
}
491+
// Fallback to execute prior handlers
492+
if (coreIntHandler != SIG_DFL && coreIntHandler != SIG_IGN) {
493+
// There's a catch here: if the prior handler was SIG_DFL we have no easy way
494+
// of invoking it here;
495+
// As that's an unlikely situation we'll just treat SIG_DFL as SIG_IGN.
496+
(*coreIntHandler)(sig);
497+
}
498+
}

0 commit comments

Comments
 (0)