Lowell Alleman wrote:
I'm running into this issue on Windows with the follow exception at
the time when the signal handler is called:
Traceback (most recent call last):
...
self.done.wait(30)
File "D:\Python24\lib\threading.py", line 348, in wait
self.__cond.wait(timeout)
File "D:\Python24\lib\threading.py", line 222, in wait
_sleep(delay)
IOError: [Errno 4] Interrupted function call
Well you've certainly picked a ticklish area to run
into problems with ;). First, forget about the
threading aspects for the moment. AFAICT the smallest
program which reproduces your problem is:
<code>
import signal
import time
def handler (*args):
pass
signal.signal(signal.SIGBREAK, handler)
time.sleep (10)
</code>
Now run that and do a ctrl-break somewhere in
that time.sleep. Sure enough...
<output>
Traceback (most recent call last):
File "C:\data\temp\sig3.py", line 8, in <module>
time.sleep (10)
IOError: [Errno 4] Interrupted function call
</output>
Under the covers, the sleep function is implemented
as a WaitForSingleObject call with a timeout. The
object being waited on is an anonymous event which
is set from a console ctrl handler when one of
those interrupts happens (ctrl-c / ctrl-break /
shutdown). This makes sure that the (otherwise
blocking) sleep can be interrupted. Something
like this:
<code>
import win32api
import win32event
hEvent = win32event.CreateEvent (None, 1, 0, None)
def handler (*args):
win32event.PulseEvent (hEvent)
return True
win32api.SetConsoleCtrlHandler (handler, 1)
#
# This line is basically the sleep happening
#
win32event.WaitForSingleObject (hEvent, 10000)
</code>
For reasons which I'm not aware of, the code
recognises that the interrupt has fired and
sets the WSAEINTR error code, which is the
IOError 4 which you're seeing, and then exits.
EINTR is usually issued when a blocking call
is cancelled explicitly, so presumably this
is considered a simulation of that. Not sure.
What happens now is that control passes
back out to the routine which called the
sleep. But... an exception condition has
been set inside the sleep call and is now
raised, appearing to come from within the
routine which called sleep.
Clear?
You could obviously continue to catch the
exception. Alternatively,
I think your best bet, as long as you can make
it work with the threads, is to install your
own console ctrl handler. Note that the docs
for this specify that:
"When the signal is received, the system creates
a new thread in the process to execute the function."
So you'll need to be quite careful to make your
code thread-safe. But it should work. I adapted
your example slightly to replace all of the lines
setting the signal handlers to one statement:
win32api.SetConsoleCtrlHandler (handler, 1)
and the handler function itself takes only one
arg, the signal no, and returns False to indicate
that control should pass to the next handler, which
will probably be the default Python handler:
def handler(arg):
print "Signal handler", arg
global workers
for w in workers:
w.stop()
return False
Hope that all helps.
TJG
--
http://mail.python.org/mailman/listinfo/python-list