[PyQt] QProcess hangup using PyQt + threads
Phil Thompson
phil at riverbankcomputing.com
Fri Feb 26 12:08:58 GMT 2010
On Fri, 26 Feb 2010 11:05:44 +0100, Denis RIVIERE <denis.riviere at cea.fr>
wrote:
> We have run into what we think to be more or less a bug in PyQt4, in
> QProcess,
> somewhat indirectly.
> QProcess sometimes hangsup in threaded PyQt applications, at least on
Linux
>
> systems.
> The situation is the following:
> - A PyQt program is using two or more threads (say, python threads)
> - one of the threads is using a QProcess (whatever thread), while the
other
> is
> using python (of course).
>
> We think we have understood what is going on:
>
> Internally, QProcess forks then probably uses a kind of exec().
> But the child forked process sometimes gets locked on the python GIL
lock.
> Then the calling process waits for the child, and also gets hung on a
> select()
> call.
>
> Actually, when forking a multi-threaded process, only the forking thread
is
>
> duplicated: so the child process has only 1 thread. But the locks,
mutexes
> and other threading items have kept the state they had in their parent
> process: some are locked, but the other threads which would normally
> release
> them do not exist in the child.
> Here, the python GIL happens to be locked by another thread in the parent
> process, and the child process seems to try to lock the GIL too. We guess
> QProcess calls a virtual method in the forked process, which is
overloaded
> by
> SIP/PyQt, which in turn calls the Python API, and finally gets hung
trying
> to
> lock the GIL.
> We have never encountered this problem using PyQt3, maybe because
QProcess
> or
> its PyQt binding may be implemented differently.
>
> This threading + fork problem is a known difficulty in fork(), explained
in
>
> the linux manpages for instance.
>
> We have 2 possible solutions for this problem:
> 1. Using pthreads, it is possible to define handlers functions that can
> take
> care of locking/unlocking the needed locks whenever fork() is called,
both
> in
> the parent and child processes: this is done using the pthread_atfork()
> function. Typically PyQt may define such handlers to take care of the GIL
> locking when fork() is used. However we don't know if such a mechanism
> exists
> on non-pthread systems (Windows?). We also don't really know if the
problem
>
> exists on Windows; possibly not because Windows doesn't use fork() but
> probably rather a spawn equivalent (?).
> 2. The problem actually happens because the python API gets called from
the
>
> forked process. Normally, in C++ Qt, the QProcess doesn't trigger such a
> locking problem. However the SIP binding defines a subclass which traps
all
>
> virtual methods calls and redirects them to python. So if we are not
using
> a
> sip-inherited QProcess class, the problem should not happen. We have made
> the
> test, making a sip-bound factory function which instantiates a QProcess
> from
> C++, then returns it to the pyton layer. This way no inherited class is
> used,
> and the problem is avoided. And it actually works.
>
> Would it be difficult to have solution 1 included in the standard PyQt ?
Unless you can provide me with a test case you'll have to experiment...
Try adding...
pthread_atfork(NULL, NULL, PyOS_AfterFork);
...after the call to PyEval_InitThreads() in siplib.c.
Phil
More information about the PyQt
mailing list