Skip to content Skip to sidebar Skip to footer

Terminating A Function Call In Python After N Seconds

my python code goes like this: def a(): ... ... subprocess.call() ... ... def b(): ... ... and so on. My task: 1) If subprocess.call

Solution 1:

My task: 1) If subprocess.call() returns within 3 seconds, my execution should continue the moment subprocess.call() returns. 2) If subprocess.call() does not return within 3 seconds, the subprocess.call() should be terminated and my execution should continue after 3 seconds. 3) Until subprocess.call() returns or 3 seconds finishes, the further execution should not take place.

On *nix, you could use signal.alarm()-based solution:

import signal
import subprocess

classAlarm(Exception):
    passdefalarm_handler(signum, frame):
    raise Alarm

# start process
process = subprocess.Popen(*your_subprocess_call_args)

# set signal handler
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(3) # produce SIGALRM in 3 secondstry:
    process.wait() # wait for the process to finish
    signal.alarm(0) # cancel alarmexcept Alarm: # subprocess does not return within 3 seconds
    process.terminate() # terminate subprocess
    process.wait()

Here's a portable threading.Timer()-based solution:

import subprocess
import threading

# start process
process = subprocess.Popen(*your_subprocess_call_args)

# terminate process in 3 secondsdefterminate():
    if process.poll() isNone:
        try:
            process.terminate()
        except EnvironmentError:
            pass# ignore 

timer = threading.Timer(3, terminate)
timer.start()
process.wait()
timer.cancel()

Solution 2:

Finally the below code worked:

import subprocess
import threading
import time


defprocess_tree_kill(process_pid):
    subprocess.call(['taskkill', '/F', '/T', '/PID', process_pid])

defmain():
    cmd = ["gcc", "-O2", "a.c", "-o", "a"];  
    p = subprocess.Popen(cmd)
    p.wait()
    print"Compiled"
    start = time.time()

    process = subprocess.Popen("a",shell=True)
    print(str(process.pid))   

    # terminate process in timeout seconds
    timeout = 3# seconds
    timer = threading.Timer(timeout, process_tree_kill,[str(process.pid)])
    timer.start()

    process.wait()
    timer.cancel()

    elapsed = (time.time() - start)
    print elapsed

if __name__=="__main__":
    main()

Solution 3:

If you're willing to convert your call to a Popen constructor instead of call (same way you are running gcc), then one way to approach this is to wait 3 seconds, poll the subprocess, and then take action based on whether its returncode attribute is still None or not. Consider the following highly contrived example:

import sys
import time
import logging
import subprocess

logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)

if __name__ == '__main__':
  logging.info('Main context started')
  procCmd = 'sleep %d' % int(sys.argv[1])
  proc = subprocess.Popen(procCmd.split())

  time.sleep(3)
  if proc.poll() isNone:
    logging.warning('Child process has not ended yet, terminating now')
    proc.terminate()
  else:
    logging.info('Child process ended normally: return code = %s' % str(proc.returncode))

  logging.info('Main context doing other things now')
  time.sleep(5)
  logging.info('Main context ended')

And this results in different logging output depending upon whether the child process completed within 3 seconds or not:

$pythonparent.py12015-01-18 07:00:56,639INFOMaincontextstarted2015-01-18 07:00:59,645INFO Child process ended normally:returncode=02015-01-18 07:00:59,645INFOMaincontextdoingotherthingsnow2015-01-18 07:01:04,651INFOMaincontextended$pythonparent.py102015-01-18 07:01:05,951INFOMaincontextstarted2015-01-18 07:01:08,957WARNINGChildprocesshasnotendedyet,terminatingnow2015-01-18 07:01:08,957INFOMaincontextdoingotherthingsnow2015-01-18 07:01:13,962INFOMaincontextended

Note that this approach above will always wait 3 seconds even if the subprocess completes sooner than that. You could convert the above into something like a loop that continually polls the child process if you want different behavior - you'll just need to keep track of how much time has elapsed.

Solution 4:

#!/usr/bin/pythonimport thread
import threading
import time
import subprocess
import os 

ret=-1defb(arg):
    global ret
    ret=subprocess.call(arg,shell=True);

thread.start_new_thread(b,("echo abcd",))
start = time.time()


while (not (ret == 0)) and ((time.time() - start)<=3):
    passif (not (ret == 0)) :
    print"failed"
    elapsed = (time.time() - start)
    print elapsed
    thread.exit()

elif (ret == 0):#ran before 3 secprint"successful"
    elapsed = (time.time() - start)
    print elapsed

I have written the above code which is working and satisfying all my contstraints. The link https://docs.python.org/2/library/thread.html says:

thread.exit() Raise the SystemExit exception. When not caught, this will cause the thread to exit silently.

So I suppose there should be no problem of orphan processes, blocked resources, etc. Please suggest.

Post a Comment for "Terminating A Function Call In Python After N Seconds"