Author njs
Recipients njs, paul.moore, steve.dower, tim.golden, zach.ware
Date 2018-12-23.03:55:56
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1545537356.71.0.0770528567349.issue35568@roundup.psfhosted.org>
In-reply-to
Content
Suppose we want to test how a program responds to control-C. We'll want to write a test that delivers a SIGINT to our process at a time of our choosing, and then checks what happens. For example, asyncio and Trio both have tests like this, and Trio even does a similar thing at run-time to avoid dropping signals in an edge case [1].

So, how can we deliver a signal to our process?

On POSIX platforms, you can use `os.kill(os.getpid(), signal.SIGINT)`, and that works fine. But on Windows, things are much more complicated: https://github.com/python/cpython/pull/11274#issuecomment-449543725

The simplest solution is to use the raise() function. On POSIX, raise(signum) is just a shorthand for kill(getpid(), signum). But, that's only on POSIX. On Windows, kill() doesn't even exist... but raise() does. In fact raise() is specified in C89, so *every* C runtime has to provide raise(), no matter what OS it runs on.

So, you might think, that's ok, if we need to generate synthetic signals on Windows then we'll just use ctypes/cffi to access raise(). *But*, Windows has multiple C runtime libraries (for example: regular and debug), and you have to load raise() from the same library that Python is linked against. And I don't know of any way for a Python script to figure out which runtime it's linked against. (I know how to detect whether the interpreter is configured in debug mode, but that's not necessarily the same as being linked against the debug CRT.) So on the one platform where you really need to use raise(), there's AFAICT no reliable way to get access to it.

This would all be much simpler if the signal module wrapped the raise() function, so that we could just do 'signal.raise_(signal.SIGINT)'. We should do that.

-------

[1] Specifically, consider the following case (I'll use asyncio terminology for simplicity): (1) the user calls loop.add_signal_handler(...) to register a custom signal handler. (2) a signal arrives, and is written to the wakeup pipe. (3) but, before the loop reads the wakeup pipe, the user calls loop.remove_signal_handler(...) to remove the custom handler and restore the original signal settings. (4) now the loop reads the wakeup pipe, and discovers that a signal has arrived, that it no longer knows how to handle. Now what? In this case trio uses raise() to redeliver the signal, so that the new signal handler has a chance to run.
History
Date User Action Args
2018-12-23 03:55:58njssetrecipients: + njs, paul.moore, tim.golden, zach.ware, steve.dower
2018-12-23 03:55:56njssetmessageid: <1545537356.71.0.0770528567349.issue35568@roundup.psfhosted.org>
2018-12-23 03:55:56njslinkissue35568 messages
2018-12-23 03:55:56njscreate