Win32 trouble with threading, signals, and sleep()

Lowell Alleman lowell at allemansonline.com
Fri Aug 8 21:17:48 EDT 2008


I'm trying to track down an issue with a multi-threaded program that
is responsible for handling real-time monitoring a business process.
Different threads track various aspects of the process and all of the
statistics filter back to the main thread for analysis.  The program
is run as a scheduled task, and various stats are shown in the console
window ("dos box") as it runs.  Normally the main thread tells the
worker threads to stop (using an Event()) when the work is done.

I just recently added some signal handlers to the program to send out
an email if someone comes along and closes the console window. (BTW,
sometimes closing the app is legitimate, so I don't want to just
disable it or run the process in the background)

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

I do development work on both Linux and Windows, and Linux doesn't
seem to behave this way.  I'm careful to make sure that the signal
handler only directly interacts with the main thread to be safe, but
the must be something I'm missing.

I took a simple threading example and modified to demonstrate the
problem.  It fails the same way, every time I run it on Windows, and
it works find each time on Linux.

I temporarly stuck in a try/except block to just ignore IOErrors, but
that seems like a faulty approach.  I would have to add try/except
block everywhere I call anything that could potentially call sleep(),
which seems like a poor (and painful) solution.  I also thought about
replacing threading._sleep with a try/except wrapper function, but
that seems kind of evil.

Thanks in advance for any suggestions,

Lowell Alleman


-----------------

import threading
import signal
import time, random

class Counter:
    def __init__(self):
        self.lock = threading.Lock()
        self.value = 0
    def increment(self):
        self.lock.acquire()
        self.value = value = self.value + 1
        self.lock.release()
        return value

class Worker(threading.Thread):
    def __init__(self):
        self.done = threading.Event()
        threading.Thread.__init__(self)
    def run(self):
        while not self.done.isSet():
            # pretend we're doing something that takes 10-100 ms
            value = counter.increment() # increment global counter
            time.sleep(random.randint(10, 100) / 1000.0)
            print self.getName(), "-- task", i, "finished", value
    def stop(self):
        self.done.set()

def handler(sig, frame):
    print "Signal handler.  Sig=%r" % sig
    global workers
    for w in workers:
        w.stop()

counter = Counter()
workers = [ Worker() for i in range(10) ]
for w in workers:
    w.start()

# Install my custom signal handler which tells worker threads to stop
try:   # Win32
    signal.signal(signal.SIGBREAK, handler)
except:   # Linux
    signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGINT, handler)

print "Please press Ctrl-C/Ctrl-Break or close the console window now..."
# Main thread sleeps waiting for work to be done...
time.sleep(60)

# Wait for all workers to finish
print "Stop All workers!"
for w in workers:
    w.stop()

print "Joining all workers to main!"
for w in workers:
    w.join()

-----------------

Traceback (most recent call last):
  File "python_thread_signal_issue.py", line 45, in ?
    time.sleep(60)
IOError: [Errno 4] Interrupted function call



More information about the Python-list mailing list