2

Hi I embeded a time constraint in to my python code which is running a fortran code with a function. However I realized that the function that puts a time constraint on the other function doesn't terminates the code, just leaves it in background and skips it instead. What I want to do is terminate the code, when it exceed the constrained time. Here is the code that I'm using to constrain time which is taken from here.

def timeout(func, args=(), kwargs={}, timeout_duration=15, default=1):
    import signal

    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

I looked up popen.terminate(), sys.exit() and atexit.register() but couldn't figure out how it will work with this piece of code which I tried to add in part below that I showed in comment.

...
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
#add the terminator
    finally:
...

NOTE: Function is inside a for loop chain so I dont want to kill whole python session but just want to kill the program that this function runs which is a fortran code and skip to the other element in the for loop chain.

Part below added after some comments and answers:

I tried to add SIGTERM with popen.terminate() however it terminated all python session which I just want to terminate current running session and skip to the other elements in the for loop. what I did is as follows:

...
    signal.signal(signal.SIGTERM, handler) 
    signal.alarm(timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
        popen.terminate()
...
Community
  • 1
  • 1
jackaraz
  • 291
  • 1
  • 12
  • what do you mean by *"the program that this function runs"*? Is it an external process started by `subprocess` module? Are there descendant processes? Does the process exit if you send SIGTERM signal? What is your Python version? It should be enough to pass `timeout` parameter to `process.wait()` and call `process.terminate()` on TimeoutError if there are no descendants and the process exits on `SIGTERM`. – jfs Mar 31 '16 at 19:50
  • @J.F.Sebastian this code runs a function which runs a fortran code which I want to limit the running time to save from cpu. All this funtion does is leaves the fortran code in the background and skips to the other element in for loop which piles up in cpu after 100 cycles lets say so I want to terminate the session before skipping to the other element. Python version 2.7. – jackaraz Apr 01 '16 at 15:44
  • Possibly related: [Run a command for a specified time and then abort if time exceeds](http://unix.stackexchange.com/q/23145) – GingerPlusPlus Apr 01 '16 at 16:09
  • @jackaraz again, does "runs a fortran code" mean that you run an external process using `subprocess` module? (E.g., `numpy` library runs plenty of fortran code *without starting a new process*). To limit the number of concurrent processes, see [this answer](http://stackoverflow.com/a/14533902/4279). – jfs Apr 01 '16 at 16:10
  • @J.F.Sebastian yes I'm using `subprocess.check_output('./../bin/SPhenoUMSSM ../UMSSM/LH_out_'+mod+' > SPheno_log_'+mod, shell=True)` in another function to run the fortran code – jackaraz Apr 01 '16 at 16:13
  • @jackaraz : where do you get `popen` then? – jfs Apr 01 '16 at 16:15
  • @J.F.Sebastian I've never used it before, were just trying the suggestions written below to see if it works. – jackaraz Apr 01 '16 at 16:25
  • Answer can be found in [this link](http://stackoverflow.com/a/39707979/3910261). – jackaraz Sep 30 '16 at 15:46

1 Answers1

1

You cannot expect the signal handler raising an Exception to get propagated through the call stack, it's invoked in a different context.

popen.terminate() will generate SIGTERM on posix systems so you should have a signal handler for SIGTERM and not SIGALRM.

Instead of raising an exception in your signal handler, you should set some variable that you periodically check in order to halt activity.

Alternatively if you don't have a signal handler for SIGTERM the default handler will probably generate a KeyboardInterrupt exception.

Brian Cain
  • 14,403
  • 3
  • 50
  • 88
  • So do you suggest to use popen.terminate() with SIGTERM? because alternative that you proposed sounds like, if I understand correctly will terminate all python session which I dont want to do because this piece is in a chain of for loops so I just want to terminate this particular session and move on to the other elements in the for loop. – jackaraz Mar 31 '16 at 15:18
  • 1- exceptions do get propagated in the main thread. The issue might be that `func()` may (due to a bug) forget to call `PyErr_CheckSignal()` on `EINTR` in its C code and therefore the Python signal handler is not called until `func()` returns control to the interpreter. 2- What is the point of sending SIGTERM to the python process. It seems OP wants to terminate a child process instead. The purpose of `SIGALRM` is to interrupt whatever python process is doing e.g., `process.wait()`. 3- if code may check a variable periodically then unless the code is in thread; it is easier to raise exception – jfs Mar 31 '16 at 19:58