[Twisted-Python] Reactor getting stuck in random places on Windows?

I'm writing a Twisted client that uses PB. It seems to be getting stuck intermittently -- failing to respond to the server's queries. Sometimes it gets stuck immediately after login, sometimes after it's been running fine for a while. I'm starting the Twisted client in a terminal window. What unsticks it in these cases is always a single ^C. Now, sometimes that spits up an unhandled exception from some place into the log (stdout) and then it goes on its merry way -- other times it has no error to report and moves on like nothing happened. I'm frequently ctrl-C'ing this program as I try my new changes, and sometimes it takes one to kill it, and sometimes it takes two. Hopefully this is a clue. I thought perhaps I had a deferred that was hanging, but in the entire client, I'm calling addCallback() exactly once -- and it's followed by an addErrback(), so I don't think the problem is there. I first noticed this behavior when it was running as a Windows service, and I thought it may have been related to the necessity of starting the reactor with calling reactor.run(installSignalHandlers=0) (I didn't yet see the tip about periodically passing child signals to the reactor), but it still happens, albeit apparently less often, with a simple reactor.run(). Any ideas would be appreciated. Thanks, Steve

On Fri, 02 Feb 2007 23:13:36 -0600, Steve Freitas <sflist@ihonk.com> wrote:
Well I don't really know what your problem is, but here are some things you can think about and see if any little lights go off in your head :) Are you running sub-processes? If a sub-process shares the same console some interesting things can happen. There isn't any way to tell which process will receive console input. See this page for what Microsoft has to say about it: http://msdn2.microsoft.com/en-us/library/ms682528.aspx In particular: "(By default, a console process inherits its parent's console, and there is no guarantee that input is received by the process for which it was intended.)" Indeed, you can verify this by running a parent process and a child process that both read bytes from stdin. You will see that the bytes are sent to one process or the other. In my testing it alternated back and forth evenly. Other forms of console input, such as ^C, seem to go to both processes at the same time. At least that is what happened in my simple test. Maybe for you it's only delivering the ^C to one of the procsses, and sometimes it's the right one and sometimes not? Or maybe it's much simpler than this and you just have a bare "except:" that is catching and hiding the first KeyboardInterrupt? As for why your process is hanging in the first place, who is to say? Maybe try running under WingIDE (or something) and break in w/ the debugger when things hang up. The stack trace ought to give you some clues. Hope that helps, -- Eric Mangold Twisted/Win32 Co-Maintainer

Thanks for your response, gives me a bunch to think about. On Sat, 2007-02-03 at 09:10 -0600, Eric Mangold wrote:
Great idea, I'll give it a go. In related news, I've decided to go back to trying to make spawnProcess() work for me. I didn't realize it at the time, but when I do subProcess.Popen(shell=True), although it solved my open console window problem, it spawned my desired process as a child to a CMD.EXE process. I didn't think that was a big deal until I figured out how hard it is under Windows without using either the Taskkill utility (and I need to run on Win98) or undocumented calls (e.g. Sysinternals) to kill a child process while being absolutely certain that's the process you want to kill. I'm going to have multiple instances of this process, so I can't kill by name, and I don't want to kill by PID, because it could get reused. I'd love to be corrected here if I'm wrong, by the way. So back it is to spawnProcess(), since it gives me a ProcessProtocol I can call transport.loseConnection() on to kill exactly what I mean to kill. To solve the console problem, I'm going to try Igor Kravtchenko's modified spawnProcess[1], and pass in CREATE_NO_WINDOW or DETACHED_PROCESS to see what happens. Steve [1]http://twistedmatrix.com/pipermail/twisted-python/2006-August/013814.html

On Sat, 2007-02-03 at 11:46 -0800, Steve Freitas wrote:
So back it is to spawnProcess(), since it gives me a ProcessProtocol I can call transport.loseConnection() on to kill
Thinko, should be transport.signalProcess('KILL')...

On Sat, 03 Feb 2007 13:46:11 -0600, Steve Freitas <sflist@ihonk.com> wrote: [...]
There really isn't any room for error if you just save the handle that was returned when you spawned the process. Just use that in a call to TerminateProcess(). -- Eric Mangold Twisted/Win32 Co-Maintainer

On Sat, 2007-02-03 at 19:02 -0600, Eric Mangold wrote:
Yeah, that's another thing that drove me back to spawnProcess(). With subprocess.Popen, I have to use shell=True to keep the terminal window hidden, and then I don't have the handle (or even the PID obtained directly) of the process that gets launched as a child of CMD.EXE, so I'm stuck with a little uncertainty when I try to kill the process. Steve

On Fri, 02 Feb 2007 23:13:36 -0600, Steve Freitas <sflist@ihonk.com> wrote:
Well I don't really know what your problem is, but here are some things you can think about and see if any little lights go off in your head :) Are you running sub-processes? If a sub-process shares the same console some interesting things can happen. There isn't any way to tell which process will receive console input. See this page for what Microsoft has to say about it: http://msdn2.microsoft.com/en-us/library/ms682528.aspx In particular: "(By default, a console process inherits its parent's console, and there is no guarantee that input is received by the process for which it was intended.)" Indeed, you can verify this by running a parent process and a child process that both read bytes from stdin. You will see that the bytes are sent to one process or the other. In my testing it alternated back and forth evenly. Other forms of console input, such as ^C, seem to go to both processes at the same time. At least that is what happened in my simple test. Maybe for you it's only delivering the ^C to one of the procsses, and sometimes it's the right one and sometimes not? Or maybe it's much simpler than this and you just have a bare "except:" that is catching and hiding the first KeyboardInterrupt? As for why your process is hanging in the first place, who is to say? Maybe try running under WingIDE (or something) and break in w/ the debugger when things hang up. The stack trace ought to give you some clues. Hope that helps, -- Eric Mangold Twisted/Win32 Co-Maintainer

Thanks for your response, gives me a bunch to think about. On Sat, 2007-02-03 at 09:10 -0600, Eric Mangold wrote:
Great idea, I'll give it a go. In related news, I've decided to go back to trying to make spawnProcess() work for me. I didn't realize it at the time, but when I do subProcess.Popen(shell=True), although it solved my open console window problem, it spawned my desired process as a child to a CMD.EXE process. I didn't think that was a big deal until I figured out how hard it is under Windows without using either the Taskkill utility (and I need to run on Win98) or undocumented calls (e.g. Sysinternals) to kill a child process while being absolutely certain that's the process you want to kill. I'm going to have multiple instances of this process, so I can't kill by name, and I don't want to kill by PID, because it could get reused. I'd love to be corrected here if I'm wrong, by the way. So back it is to spawnProcess(), since it gives me a ProcessProtocol I can call transport.loseConnection() on to kill exactly what I mean to kill. To solve the console problem, I'm going to try Igor Kravtchenko's modified spawnProcess[1], and pass in CREATE_NO_WINDOW or DETACHED_PROCESS to see what happens. Steve [1]http://twistedmatrix.com/pipermail/twisted-python/2006-August/013814.html

On Sat, 2007-02-03 at 11:46 -0800, Steve Freitas wrote:
So back it is to spawnProcess(), since it gives me a ProcessProtocol I can call transport.loseConnection() on to kill
Thinko, should be transport.signalProcess('KILL')...

On Sat, 03 Feb 2007 13:46:11 -0600, Steve Freitas <sflist@ihonk.com> wrote: [...]
There really isn't any room for error if you just save the handle that was returned when you spawned the process. Just use that in a call to TerminateProcess(). -- Eric Mangold Twisted/Win32 Co-Maintainer

On Sat, 2007-02-03 at 19:02 -0600, Eric Mangold wrote:
Yeah, that's another thing that drove me back to spawnProcess(). With subprocess.Popen, I have to use shell=True to keep the terminal window hidden, and then I don't have the handle (or even the PID obtained directly) of the process that gets launched as a child of CMD.EXE, so I'm stuck with a little uncertainty when I try to kill the process. Steve
participants (2)
-
Eric Mangold
-
Steve Freitas