Mailing List Archive

Avoid nested SIGINT handling
Hi!

How do I handle a SIGINT (or any other signal) avoid nesting?

Does this work?

class STATUS:
InInt=False

def SIGINT_handler(sn,f):
if STATUS.InInt: return
STATUS.InInt=True
process_int()
STATUS.InInt=False

Thanks for any suggestions.
Paulo
--
https://mail.python.org/mailman/listinfo/python-list
Re: Avoid nested SIGINT handling [ In reply to ]
Às 06:22 de 11/11/21, Chris Angelico escreveu:
> On Thu, Nov 11, 2021 at 5:01 PM Jon Ribbens via Python-list
> <python-list@python.org> wrote:
>>
>> On 2021-11-10, Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> wrote:
>>> Hi!
>>>
>>> How do I handle a SIGINT (or any other signal) avoid nesting?
>>
>> I don't think you need to. Python will only call signal handlers in
>> the main thread, so a handler can't be executed while another handler
>> is running anyway.
>
> Threads aren't the point here - signals happen immediately.
>
> Would it be easier to catch KeyboardInterrupt and do your processing
> there, rather than actually catching SIGINT?
>
> I'd recommend just trying what you have, and seeing if it's reentrant.
> My suspicion is that it isn't, on a technical level (the Python
> function will be queued for when it's safe to call it - probably after
> the next bytecode instruction), but that your own code will still need
> to worry about reentrancy.
>
OK, thank you
--
https://mail.python.org/mailman/listinfo/python-list
Re: Avoid nested SIGINT handling [ In reply to ]
On Thu, 11 Nov 2021 17:22:15 +1100, Chris Angelico wrote:

> Threads aren't the point here - signals happen immediately.

Actually, signals are not delivered immediately. Signals are delivered
the next time the process gets its turn on CPU. The process scheduler
will make process runnable and the process will check for any pending
signals first and will execute the handler. It is possible to have
several SIGINT signals pending, for instance when I nervously press ctrl-
C several times. However, signals are not processed as a part of the
normal flow of the process and are processed sequentially.. When the
process finds a pending signal, it executes the registered signal
handler. It's always the same signal handler, unless signal handler is
changed within the signal handler. After the signals are delivered, the
process continues its normal operation until its CPU quantum expires or
until initiates a synchronous I/O operation, as is the case with all
normal read operations.
BTW, that's the case on both Unix/Linux systems and Windows systems.



--
Mladen Gogala
Database Consultant
https://dbwhisperer.wordpress.com
--
https://mail.python.org/mailman/listinfo/python-list
Re: Avoid nested SIGINT handling [ In reply to ]
On Sun, Nov 14, 2021 at 4:42 AM Mladen Gogala via Python-list
<python-list@python.org> wrote:
>
> On Thu, 11 Nov 2021 17:22:15 +1100, Chris Angelico wrote:
>
> > Threads aren't the point here - signals happen immediately.
>
> Actually, signals are not delivered immediately. Signals are delivered
> the next time the process gets its turn on CPU. The process scheduler
> will make process runnable and the process will check for any pending
> signals first and will execute the handler. It is possible to have
> several SIGINT signals pending, for instance when I nervously press ctrl-
> C several times. However, signals are not processed as a part of the
> normal flow of the process and are processed sequentially.. When the
> process finds a pending signal, it executes the registered signal
> handler. It's always the same signal handler, unless signal handler is
> changed within the signal handler. After the signals are delivered, the
> process continues its normal operation until its CPU quantum expires or
> until initiates a synchronous I/O operation, as is the case with all
> normal read operations.
> BTW, that's the case on both Unix/Linux systems and Windows systems.
>

Maybe at the C level, but none of that happens in Python :)

There's a very very small signal handler in CPython that just sets a
flag and then handles things separately.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: Avoid nested SIGINT handling [ In reply to ]
On 11/12/21, Mladen Gogala via Python-list <python-list@python.org> wrote:
> On Thu, 11 Nov 2021 17:22:15 +1100, Chris Angelico wrote:
>
>> Threads aren't the point here - signals happen immediately.
>
> [snip: description of POSIX signals]
>
> BTW, that's the case on both Unix/Linux systems and Windows systems.

Windows is quite different. The base NT system has nothing that's
closely analogous to POSIX signals. Its asynchronous procedure call
(APC) capability is roughly similar to POSIX signals. Every thread has
two APC queues, one for kernel-mode APCs and one for user-mode APCs.
Unlike a POSIX signal, queuing an APC requires the address of a
function in the process of the target thread. This requires well-known
or registered function targets. User-mode APCs can be queued to a
thread via QueueUserAPC(). Unless forced, they execute when the thread
does an alertable wait.

Another similar feature is a window message. A thread that uses any
window-manager function is converted to a GUI thread with a message
queue. A thread's window message queue is typically processed during a
GetMessage() or PeekMessage() call. A message can be queued directly
to a thread via PostThreadMessage(), provided it's a GUI thread.

There's an overlap between Windows exceptions and POSIX signals.
Exceptions get raised either from the kernel or RaiseException().
Exceptions have stack based handlers that are set via Microsoft's
extended C __try/__except and __try/__finally statements. Process-wide
exception handlers can also be set via AddVectoredExceptionHandler()
and SetUnhandledExceptionFilter().

Windows also has console control events, which the console host
(conhost.exe or openconsole.exe) sends to process groups that are
connected to the hosted session (i.e. a TUI window/tab on a GUI
desktop). The console host generates these events in response to
typing Ctrl+C (cancel) or Ctrl+Break in the window/tab, closing the
window/tab, user logoff, and system shutdown.

CTRL_C_EVENT (0)
CTRL_BREAK_EVENT (1)
CTRL_CLOSE_EVENT (2)
CTRL_LOGOFF_EVENT (5)
CTRL_SHUTDOWN_EVENT (6)

The Ctrl+C and Ctrl+Break events can also be generated for a process
group via GenerateConsoleCtrlEvent(ctrlEvent, processGroupId).

There's no thread or process queue for console control events. To
deliver an event, the console host sends a message to the Windows
session server (csrss.exe) that contains the control event number and
one or more target processes. The session server creates a new thread
in each target process. For example, if the user presses Ctrl+C five
times sequentially in the console window, csrss.exe creates five
threads in every process that's attached to the console. This thread
injection is so different from POSIX signals that I hesitate to group
it with signal-like features.

A console control thread begins executing at the CtrlRoutine()
function in kernelbase.dll. It sequentially iterates the list of
registered handlers. Each is called with the given control event. If a
handler returns TRUE (i.e. handled), the control thread exits. If no
handler returns TRUE, the default handler is called, which exits the
process with the exit code STATUS_CONTROL_C_EXIT (0xC000013A). A
inheritable flag can be set to disable CTRL_C_EVENT, such that
CtrlRoutine() basically does nothing for this event.

For CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT, the
process must be terminated after the configured timeout, which is 5
seconds in a default setup. The process can exit gracefully of its own
accord before the timeout.

The C runtime library in Windows emulates some signals that are
required by the language standard.

The following two are emulated for C raise() and abort():

SIGABRT - abort, abnormal termination
SIGTERM - terminate

The following three are based on an exception handler:

SIGSEGV - segmentation violation
SIGILL - illegal/invalid instruction
SIGFPE - floating point exception

The following two are based on a console control event handler
(SIGBREAK is non-standard):

SIGINT - interrupt
SIGBREAK - break, close, logoff, shutdown

For SIGINT and SIGBREAK, the C runtime uses CTRL_C_EVENT and CTRL_BREAK_EVENT.

It also maps SIGBREAK to CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and
CTRL_SHUTDOWN_EVENT. But Python's C signal handler just sets a flag
and returns, so SIGBREAK due to those events can't be handled with
Python's signal module. The process gets terminated long before the
main thread can call Python's registered SIGBREAK handler.
--
https://mail.python.org/mailman/listinfo/python-list