[Python-ideas] Responsive signal handling
Cameron Simpson
cs at zip.com.au
Tue Jun 23 01:00:41 CEST 2015
On 22Jun2015 01:42, Devin Jeanpierre <jeanpierreda at gmail.com> wrote:
>On Mon, Jun 22, 2015 at 12:52 AM, Cameron Simpson <cs at zip.com.au> wrote:
>> On 21Jun2015 19:16, Devin Jeanpierre <jeanpierreda at gmail.com> wrote:
>>> Operations that run in C but just
>>> take a long time, or that are part of third-party code, will continue
>>> to inhibit responsiveness.
>>>
>>> - Run signal handlers in a dedicated separate thread.
>>>
>>> IMO this is generally better than running signal handlers in the main
>>> thread, because it eliminates the separate concept of "async-safe" and
>>> just requires "thread-safe". So you can use regular threading
>>> synchronization primitives for safety, instead of relying on luck /
>>> memorized lists of atomic/re-entrant operations.
>>
>> Yes, I am in favour of this or something like it. Personally I would go for
>> either or both of:
>>
>> - a stdlib function to specify the thread to handle signals instead of main
>
>This just moves the problem to another thread. One can already today
>try to keep the main thread free to handle signals, it's just hard.
Yes. But it is very easy to ensure that a specifial purpose Thread is free to
handle signals. And it is arguably the minimalist change.
>> - a stdlib function to declare that signals should immediately place a nice
>> descriptive "signal" object on a Queue, and leaves it to the user to handle
>> the queue (for example, by spawning a thread to consume it)
>
>I like this. It mirror's Linux's selectfd, too. One small correction,
>it can't literally be a Queue, because those aren't safe to use in
>signal handlers. (It can be a pipe that is wrapped in a Queue-like
>interface, though, and if we do that, we can even use native signalfd
>if we want.)
>
>It also resolves an unspoken concern I had, which is that silently
>starting threads for the user feels icky.
I wasn't proposing silently starting threads. I imagined the former suggestion
would be handed a thread as the signal target.
>>> Something still needs to run in the main thread though, for e.g.
>>> KeyboardInterrupt, so this is not super straightforward.
>>
>> Is this necessarily true?
>
>What I mean is that there needs to be a way to raise KeyboardInterrupt
>in the main thread from a signal handler. If, as you suggest, the old
>behavior stays around, then that's enough.
I was imagining the old behaviour stayed around by default, not necessarily as
fixed behaviour. But "KeyboardInterrupt occurs in the main thread" is handy.
Perhaps a better solution here is not to keep KeyboardInterrupt special (i.e.
always going to the main thread) but to extend "raise" to accept a thread
argument:
raise blah in thread
Given that signals are already presented as occuring between Python opcodes, it
seems reasonable to me that the signal situation could be addressed with a
common mechanism extended to exceptions.
How often is the question "how do I terminate another thread?" raised on
python-list? Often. The standard answer is "set a flag and have the thread
consult it". That is very sensitive to how often the flag is polled: too often
and it infuses the code with noise (not to mention ungainly loop termination
logic etc), too infrequently and the response is much like your issue here with
signals: it can be arbitrarily delayed.
Suppose one could raise signals in another thread? Then the answer becomes
"raise exception in other_thread". And the other thread will abort as soon as
the next python opcode would fire.
It has several advantages:
it removes any need to poll some shared state, or the set up shared state
it lets the target thread remain nice and pythonic, letting unhandled
exceptions simply abort the thread automatically as they would anyway
it lets the target thread catch the exception and handle it if desired
it dovetails neatly with our hypothetical special signal handling thread: the
handling thread has merely to "raise KeyboardInterrupt in main_thread" to get
the behaviour you seek to preserve, _without_ making SIGINT specially handled
- the specialness is not an aspect of the handling thread's code, not
hardwired
>Another option, if we went with a dedicated signal handling thread,
>would be that uncaught exceptions propagate to the main thread when it
>gets around to it.
Perhaps. But I'd rather not; you _can_ always catch every exception and if we
have "raise exception in thread" we can implement the above trivially for
programs which want it.
Cheers,
Cameron Simpson <cs at zip.com.au>
Nothing is impossible for the man who doesn't have to do it.
More information about the Python-ideas
mailing list