<div dir="ltr"><div>Thank you Nathaniel for the response!<br></div>Really interesting and helpful.<br><br><br><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">2017-08-08 20:51 GMT+02:00 Nathaniel Smith <span dir="ltr"><<a href="mailto:njs@pobox.com" target="_blank">njs@pobox.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Tue, Aug 8, 2017 at 2:54 AM, Jonathan Slenders <<a href="mailto:jonathan@slenders.be">jonathan@slenders.be</a>> wrote:<br>
> Hi all,<br>
><br>
> Is it possible that thread.join() cannot be interrupted on Windows, while it<br>
> can be on Linux?<br>
> Would this be a bug, or is it by design?<br>
><br>
><br>
> import threading, time<br>
> def wait():<br>
>     time.sleep(1000)<br>
> t = threading.Thread(target=wait)<br>
> t.start()<br>
> t.join()  # Press Control-C now. It stops on Linux, while it hangs on<br>
> Windows.<br>
<br>
</span>This comes down to a difference in how the Linux and Windows low-level<br>
APIs handle control-C and blocking functions: on Linux, the default is<br>
that any low-level blocking function can be interrupted by a control-C<br>
or any other random signal, and it's the calling code's job to check<br>
for this and restart it if necessary. This is annoying because it<br>
means that every low-level function call inside the Python interpreter<br>
has to have a bunch of boilerplate to detect this and retry, but OTOH<br>
it means that control-C automatically works in (almost) all cases. On<br>
Windows, they made the opposite decision: low-level blocking functions<br>
are never automatically interrupted by control-C. It's a reasonable<br>
design choice. The advantage is that sloppily written programs tend to<br>
work better -- on Linux you kind of *have* to put a retry loop around<br>
*every* low level call or your program will suffer weird random bugs,<br>
and on Windows you don't.<br>
<br>
But for carefully written programs like CPython this is actually<br>
pretty annoying, because if you *do* want to wake up on a control-C,<br>
then on Windows that has to be laboriously implemented on a<br>
case-by-case basis for each blocking function, and often this requires<br>
some kind of cleverness or is actually impossible, depending on what<br>
function you want to interrupt. At least on Linux the retry loop is<br>
always the same.<br>
<br>
The end result is that on Windows, control-C almost never works to<br>
wake up a blocked Python process, with a few special exceptions where<br>
someone did the work to implement this. On Python 2 the only functions<br>
that have this implemented are time.sleep() and<br>
multiprocessing.Semaphore.<wbr>acquire; on Python 3 there are a few more<br>
(you can grep the source for _PyOS_SigintEvent to find them), but<br>
Thread.join isn't one of them.<br>
<br>
It looks like Thread.join ultimately ends up blocking in<br>
Python/thread_nt.h:<wbr>EnterNonRecursiveMutex, which has a maze of #ifdefs<br>
behind it -- I think there are 3 different implementation you might<br>
end up with, depending on how CPython was built? Two of them seem to<br>
ultimately block in WaitForSingleObject, which would be easy to adapt<br>
to handle control-C. Unfortunately I think the implementation that<br>
actually gets used on modern systems is the one that blocks in<br>
SleepConditionVariableSRW, and I don't see any easy way for a<br>
control-C to interrupt that. But maybe I'm missing something -- I'm<br>
not a Windows expert.<br>
<span class="HOEnZb"><font color="#888888"><br>
-n<br>
<br>
--<br>
Nathaniel J. Smith -- <a href="https://vorpus.org" rel="noreferrer" target="_blank">https://vorpus.org</a><br>
</font></span></blockquote></div><br></div>