@@ -73,12 +73,19 @@ PyObject *PLy_interp_globals = NULL;
73
73
/* this doesn't need to be global; use PLy_current_execution_context() */
74
74
static PLyExecutionContext * PLy_execution_contexts = NULL ;
75
75
76
+ /* postgres backend handler for interruption */
77
+ static pqsigfunc coreIntHandler = 0 ;
78
+ static void PLy_handle_interrupt (int sig );
79
+
76
80
77
81
void
78
82
_PG_init (void )
79
83
{
80
84
int * * bitmask_ptr ;
81
85
86
+ /* Catch and process SIGINT signals */
87
+ coreIntHandler = pqsignal (SIGINT , PLy_handle_interrupt );
88
+
82
89
/*
83
90
* Set up a shared bitmask variable telling which Python version(s) are
84
91
* loaded into this process's address space. If there's more than one, we
@@ -418,6 +425,9 @@ PLy_get_scratch_context(PLyExecutionContext *context)
418
425
return context -> scratch_ctx ;
419
426
}
420
427
428
+ /* Indicate tha a python interruption is pending */
429
+ static int PLy_pending_interrupt = 0 ;
430
+
421
431
static PLyExecutionContext *
422
432
PLy_push_execution_context (void )
423
433
{
@@ -442,7 +452,47 @@ PLy_pop_execution_context(void)
442
452
443
453
PLy_execution_contexts = context -> next ;
444
454
455
+ if (PLy_execution_contexts == NULL ) {
456
+ // Clear pending interrupts when top level context exits
457
+ PLy_pending_interrupt = 0 ;
458
+ }
459
+
445
460
if (context -> scratch_ctx )
446
461
MemoryContextDelete (context -> scratch_ctx );
447
462
pfree (context );
448
463
}
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