7

I'm using a recipe that relies on SIGALRM to set alarm interrupt -- Using module 'subprocess' with timeout

The problem is that I have more than one Python script using signal.ALARM process to set time-outs, and only the latest alarm gets called. What is a good way to improve this multiple Python functions setting time-outs?

1 Answer 1

11

Except for simple, quick hacks, avoid SIGALRM. It's a very old, limited mechanism, not suited to anything more complex: you can only set a single alarm, and it interrupts any system call at the time rather than just the one you intend to interrupt.

It's much cleaner to use a timeout thread to kill the process, for example:

import subprocess, signal, os, threading, errno from contextlib import contextmanager class TimeoutThread(object): def __init__(self, seconds): self.seconds = seconds self.cond = threading.Condition() self.cancelled = False self.thread = threading.Thread(target=self._wait) def run(self): """Begin the timeout.""" self.thread.start() def _wait(self): with self.cond: self.cond.wait(self.seconds) if not self.cancelled: self.timed_out() def cancel(self): """Cancel the timeout, if it hasn't yet occured.""" with self.cond: self.cancelled = True self.cond.notify() self.thread.join() def timed_out(self): """The timeout has expired.""" raise NotImplementedError class KillProcessThread(TimeoutThread): def __init__(self, seconds, pid): super(KillProcessThread, self).__init__(seconds) self.pid = pid def timed_out(self): try: os.kill(self.pid, signal.SIGKILL) except OSError as e: # If the process is already gone, ignore the error. if e.errno not in (errno.EPERM, errno. ESRCH): raise e @contextmanager def processTimeout(seconds, pid): timeout = KillProcessThread(seconds, pid) timeout.run() try: yield finally: timeout.cancel() def example(): proc = subprocess.Popen(["sleep", "5"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) with processTimeout(1, proc.pid): print proc.communicate() resultcode = proc.wait() if resultcode < 0: print "error: %i" % resultcode if __name__ == '__main__': example() 

Depending on what you're timing out, you may want to use a lighter signal than SIGKILL to allow the timing-out process to clean up after itself.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.