views:

152

answers:

1

If I try to spawn a process from a pylons controller, the server does not close the connection after sending the reply.

Assume that test.py is a long running process, then this method in a pylons controller creates a response, but keeps the connection open:

def index(self):
    from subprocess import Popen
    Popen(["python", "/temp/test.py"])

    return "<h1>Done!</h1>"

Moving the Popen to a new thread did not help.

Luckily, I found a workaround: I start a new thread and add a sleep before the Popen. If the Popen starts after the response has been sent, the connection will be closed properly.

def test(self):
    def worker():
        import time
        time.sleep(5)
        from subprocess import Popen
        Popen(["python", "/temp/test.py"])
    from threading import Thread
    Thread(target=worker).start()

    return "<h1>Done!</h1>"

Can anyone explain this behavior? I'd like to be sure I won't be causing strange problems down the line.

I'm using Python 2.5 and Pylons 0.9.6.1 on Windows XP SP3.

UPDATE: bnonlan's answer is definitely on the right track. Popen has a parameter named close_fds which is supposed to solve this problem. In the Python 2.5 version of the subprocess module, this parameter is unsupported on Windows. However, in the Python 2.6 version you can set this parameter to True if you don't redirect stdin/stdout/stderr.

def index(self):
    # I copied the 2.6 version of subprocess.py into my tree
    from python26.subprocess import Popen
    Popen(["python", "/temp/test.py"], close_fds=True)

    return "<h1>Done!</h1>"

Sadly, I do want to redirect stdout, so I need to find another solution. Also, this implies that the workaround I found might keep other requests from closing if they happen to be running when the Popen executes. This is troubling.

+3  A: 

I don't know if this applies on windows, but on unixes, it's possible that the socket might not be marked for close-on-exec, meaning it gets inherited by the child process. If you're using cygwin, this would definitely apply to you.

The solution is either to make sure you mark all of your sockets and open files that aren't need by the child as close-on-exec (eg, as this code in Zope does), or run around closing everything with a file descriptor above 2 after the fork, but right before execing.

bdonlan
Popen has a parameter called close_fds which supposedly does exactly that. Alas, it doesn't work on Windows.
itsadok