views:

226

answers:

2

I wonder if it is possible to shut down the communication pipe when killing a subprocess started in a different thread. If I do not call communicate() then kill() will work as expected, terminating the process after one second instead of five.

I found a discussion of a similar problem here, but I got no real answers. I assume that I either have to be able to close the pipe or to explicitly kill the sub-subprocess (which is "sleep" in the example) and kill that to unblock the pipe.

I also tried to find the answer her on SO, but I only found this and this and this, which do not directly address this problem as far as I can tell (?).

So the thing I want to do is to be able to run a command in a second thread and get all its output, but be able to kill it instantly when I so desire. I could go via a file and tail that or similar, but I think there should be a better way to do this?

import subprocess, time
from threading import Thread

process = None

def executeCommand(command, runCommand):
    Thread(target=runCommand, args=(command,)).start()

def runCommand(command):
    global process
    args = command.strip().split()
    process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)

    for line in process.communicate():
        if line:
            print "process:", line,

if __name__ == '__main__':
    executeCommand("./ascript.sh", runCommand)
    time.sleep(1)
    process.kill()

This is the script:

#!/bin/bash
echo "sleeping five"
sleep 5
echo "slept five"

Output

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s
+1  A: 

It looks like you may be a victim of Python's super coarse grained concurrency. Change your script to this:

#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"

And then the output becomes:

process: sleeping five

real    0m5.134s
user    0m0.000s
sys     0m0.010s

If the entire script ran, the time would be 10s. So it looks like the python control thread doesn't actually run until after the bash script sleeps. Similarly, if you change your script to this:

#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"

Then the output becomes:

process: sleeping five

real    0m1.150s
user    0m0.010s
sys     0m0.020s

In short, your code works as logically implemented. :)

The sleep in the example is just a placeholder for any lengthy operation being done in the script. The question is, how do I get the pipe to shut down so that the code does not hang on communcate()? Alternatively, how do I find out which "sub-subprocesses" to kill to release the block?
icecream
Do you mean it's the Global Interpreter Lock hard at work? http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock
Mike Mazur
Could be? Any suggestions?
icecream
I don't know if the issue is the GIL or some interaction with the OS scheduling. I just know that the python is not waiting for the bash script to terminate, as evidenced by my example.You could try running this on an alternative python implementation. That would be a good diagnostic, if not a practical solution:http://www.python.org/dev/implementations/
A: 

It seems to me that the easiest way to do this and sidestep the multithreading problems would be to set a kill flag from the main thread, and check for it in the script-running thread just before the communication, killing the script when the flag is True.

bluejeansummer
Actually, it is more of a problem with blocking pipes than a multithreading problem. I need a different thread so that I can shut it down from somewhere. If I do "kill" from the shell, it kills the script and its subprocesses. Python's kill fails to do this because it seems to want the pipe to shut down.
icecream