1

I am trying to implement a function that will timeout if it fails to complete in a certain amount of time (having issues with a thread hanging). This answer seemed the most promising, effectively providing an API wrapper with which to call my actual function. It generally seems to work (see loop_forever), but fails to stop when I use time.sleep. Why does the timeout not work with the sleep timer and will this work as a way to prevent threads from hanging?

import signal
import time

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):


    class TimeoutError(Exception):
        pass

    def handler(signum, frame):
        raise TimeoutError()

    # set the timeout handler
    signal.signal(signal.SIGALRM, handler) 
    signal.alarm(timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
    finally:
        signal.alarm(0)

    return result




def loop_forever():
    x = 0
    while True:
        print x
        try:
            x += 1
        except:
            continue

def loop_forever_sleep():
    x = 0
    while True:
        print x
        try:
            x += 1
            time.sleep(10)
        except:
            continue

if __name__ == '__main__':
    a = timeout(loop_forever) #Terminates
    b = timeout(loop_forever_sleep) #Does not terminate
Community
  • 1
  • 1
Michael
  • 13,244
  • 23
  • 67
  • 115

1 Answers1

1

The problem is that the SIGALRM is being raised while you're inside of the time.sleep(10) system call, which causes your handler to be called in that context, too. However, you're swallowing all the exceptions that happen inside of your infinite loop, so the TimeoutError never makes it back to the timeout function, instead it gets ignored by the except: continue code inside of loop_forever_sleep. Just remove the try/except block from the while True loop and it will work fine:

def loop_forever_sleep():
    x = 0
    while True:
        print x
        x += 1
        time.sleep(10)
dano
  • 91,354
  • 19
  • 222
  • 219
  • So that means that anytime the timout occurs in a try block it will not get back to the API, just end that try block? – Michael Oct 30 '14 at 00:18
  • 1
    @Michael If you use bare `except` blocks, which will trap the `TimeoutError`, yes. You'll need to either only catch the specific exceptions you're expecting in your `try` blocks, or always re-raise `TimeoutError` if you catch it. – dano Oct 30 '14 at 01:12