Re: Interrupting long external library calls - Mailing list pgsql-hackers

From Florian Pflug
Subject Re: Interrupting long external library calls
Date
Msg-id FB27B9B7-CE47-4675-91D7-0D6161548B5D@phlo.org
Whole thread Raw
In response to Re: Interrupting long external library calls  (Sandro Santilli <strk@keybit.net>)
Responses Re: Interrupting long external library calls
List pgsql-hackers
On May24, 2012, at 15:04 , Sandro Santilli wrote:
> On Wed, May 16, 2012 at 07:30:03PM +0300, Heikki Linnakangas wrote:
>> On 16.05.2012 15:42, Sandro Santilli wrote:
>>> But CHECK_FOR_INTERRUPTS doesn't return, right ?
>>> Is there another macro for just checking w/out yet acting upon it ?
>>
>> Hmm, no. CHECK_FOR_INTERRUPTS() checks the InterruptPending
>> variable, but on Windows it also checks for
>> UNBLOCKED_SIGNAL_QUEUE(). And even if InterruptPending is set, it's
>> not totally certain that CHECK_FOR_INTERRUPTS() won't return. I
>> think InterruptPending can be set spuriously (even if that's not
>> possible today, I wouldn't rely on it), and if you're in a
>> HOLD/RESUME_INTERRUPTS block, CHECK_FOR_INTERRUPTS() will do nothing
>> even if InterruptPending is true.
>>
>> The only sane way to make 3rd party code interruptible is to add
>> CHECK_FOR_INTERRUPTS() to it, in safe places.
>
> No place is safe if CHECK_FOR_INTERRUPTS doesn't return.
> How could caller code cleanup on interruption ?

The postgres way is to use PG_TRY/PG_CATCH to make sure stuff gets cleaned
up if an error or an interrupts occurs. You could use those to make the
third-party library exception safe, but it'll probably be a quite
invasive change :-(.

Alternatively, you could replicate the check CHECK_FOR_INTERRUPTS() does,
but then don't actually call ProcessInterrupts() but instead just make
the third-party code abort signalling an error, and call
CHECK_FOR_INTERRUPTS() after the third-party code has returned. On unix-like
systems that'd be a simple as aborting if InterruptPending is set, while
on windows you'd have to do the if(UNBLOCKED_SIGNAL_QUEUE())… stuff first,
since otherwise InterruptPending will never get set. The following function
should do the trick
 bool have_pending_interrupts() { #ifdef WIN32   if (UNBLOCKED_SIGNAL_QUEUE())     pgwin32_dispatch_queued_signals();
#endif
   return InterruptPending && !InterruptHoldoffCount && !CritSectionCount; }

The third-party could would then do
 if (have_pending_interrupts())   return some_error;

and you'd invoke in with
 state = third_party_code(); CHECK_FOR_INTERRUPTS(); if (state != success)   ereport(…);

There might be slim chance for false positives with that approach, since
ProcessInterrupts() might not always ereport(), even if InterruptHoldoffCount
and CritSectionCount are zero. But they'll simply get turned into spurious
ereport() by the if(state != success) check after CHECK_FOR_INTERRUPTS, and
should be very rare, and happen only shortly after a query cancel request
was received.

best regards,
Florian Pflug



pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: shared_preload_libraries path
Next
From: Magnus Hagander
Date:
Subject: Re: pg_stat_statments queryid