Bug 219787 - krosspython: crash when PyQt signal is connected to C++ method that calls a function in the script
Summary: krosspython: crash when PyQt signal is connected to C++ method that calls a f...
Status: RESOLVED FIXED
Alias: None
Product: bindings
Classification: Developer tools
Component: general (show other bugs)
Version: unspecified
Platform: Compiled Sources Linux
: NOR crash
Target Milestone: ---
Assignee: kde-bindings
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-12-23 07:22 UTC by Daniel Calviño Sánchez
Modified: 2012-09-21 19:15 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments
Patch for unittest.py and TestObject showing the bug (3.08 KB, patch)
2009-12-23 07:27 UTC, Daniel Calviño Sánchez
Details
Patch for pythonscript.cpp with a workaround to the bug (1.36 KB, patch)
2009-12-23 07:29 UTC, Daniel Calviño Sánchez
Details
Patch for unittest.py and TestObject showing the bug (4.71 KB, patch)
2010-01-19 23:23 UTC, Daniel Calviño Sánchez
Details
Patch for pythonscript.cpp with a workaround to the bug (2.51 KB, patch)
2010-01-19 23:46 UTC, Daniel Calviño Sánchez
Details
Patch for unittest.py and TestObject showing the bug (4.72 KB, patch)
2012-06-20 18:08 UTC, Daniel Calviño Sánchez
Details
Patch for pythonscript.cpp with a workaround to the bug (2.72 KB, patch)
2012-06-20 18:15 UTC, Daniel Calviño Sánchez
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Calviño Sánchez 2009-12-23 07:22:18 UTC
Version:            (using Devel)
Compiler:          gcc 4.4.1 
OS:                Linux
Installed from:    Compiled sources

In PyQt, signals can be added to Python objects. Since PyQt 4.5, those signals are also added to the class's QMetaObject (http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html#defining-new-signals-with-qtcore-pyqtsignal), so they can be connected even from C++ code.

I have found a crash in a very specific scenario. A signal is defined in a Python class that inherits from QObject, and an object from that class is created. There is a C++ method, invokable from the script, that receives a QObject and the name of a signal. That C++ method does a C++ connect, connecting the signal in the Python object with a C++ slot. When executed, the slot calls a function in the script using the Kross Action. I know, it's a weird scenario, but I use it ;)

So, when the Python signal is emitted, the C++ slot is invoked, and it calls another Python function. The result is a crash.

In the next comments I'm going to post a unit test showing the problem and a workaround for it.
Comment 1 Daniel Calviño Sánchez 2009-12-23 07:27:59 UTC
Created attachment 39277 [details]
Patch for unittest.py and TestObject showing the bug

Here it is a unit test showing the problem.

I have added some attributes and slots to TestObject. There is a slot to connect the Python signal from C++, connectCallTestFunction. It adapts the pure signal name to a name understood by connect (signals start by "2").

The signal is connected to another slot, callTestFunction, which calls the function in the script (managed by the Kross Action stored in the attribute) named "testFunction" with parameter "42".

Finally, another slot returns the value returned by "testFunction" when it was called, that should be a QVariantList with just one integer element, 42 (as testFunction returns the argument it received). This isn't needed to show the crash, as it happens when the function is called, but I added it to have something to assert once the crash was fixed.

The test method in unittest.py first checks that PyQt is installed and its version is 4.5 or greater. Otherwise, the test is skipped.

Then it just sets the scenario that triggers the crash, as explained before.
Comment 2 Daniel Calviño Sánchez 2009-12-23 07:29:36 UTC
Created attachment 39278 [details]
Patch for pythonscript.cpp with a workaround to the bug

Although the crash itself happens in Python libraries, the backtrace goes through pythonscript.cpp before diving into Python libraries. I take a look to the las Kross executed method (PythonScript::callFunction(const QString& name, const QVariantList& args)) and found "//TODO do we need to acquire interpreter lock here?". So I tried to lock the interpreter where the crash happened (not in the whole method, as the TODO suggested).

I wrapped PyErr_Clear() with a lock, and then the method was executed further before crashing again. So then I locked the next place where it happened (Py::Object pyresult = funcobject.apply(PythonType<QVariantList,Py::Tuple>::toPyObject(args));), and then again in QVariant result = PythonType<QVariant>::toVariant(pyresult); (which uses the same lock as the previous sentence because they were one after the other).

After locking the interpreter in those places, all was fine. No more crashes, and the script behaved as expected. Note, however, that some locks may be needed in the catch clause (I haven't tested it with code that launched exceptions).

And also, please take into account that I have no true idea of what I did. I mean, I locked the interpreter using the code found somewhere else in pythonscript.cpp, and I locked it because there was a TODO suggesting it. But I don't know if it is the best fix, or if it is a fix at all and not just a workaround. I only know that locking the interpreter no crash happens ;)
Comment 3 Daniel Calviño Sánchez 2010-01-11 16:53:44 UTC
Any chance to get this crash fixed (or at least workarounded like shown in the patch) before KDE SC 4.4.0 release? :)
Comment 4 Daniel Calviño Sánchez 2010-01-19 23:23:48 UTC
Created attachment 40061 [details]
Patch for unittest.py and TestObject showing the bug

I have extended the unit tests shown in comment #1 to check also what happens when the Python function called raises an exception.

The assert in this case checks that no value was returned by the function, as the return value is set by Kross to a QVariant() when there is a problem execution a Python function.

The rest of the things explained in comment #1 still apply here.
Comment 5 Daniel Calviño Sánchez 2010-01-19 23:46:08 UTC
Created attachment 40062 [details]
Patch for pythonscript.cpp with a workaround to the bug

I have updated the workaround/fix shown in comment #2 to also handle when exceptions are raised in the Python function.

For what I could see, all the statements (but the krosswarning and "QStringList trace;") in the catch clause had to be locked, or the test crashed.

Also, the lock acquired in the try clause before calling the Python function had to be released. I opted to wrap the call with another try/catch, release the lock in the catch and throw again the exception, so it got caught again in the outer catch.

As there are several other statements in the outer try clause, I assume that the Py::Exception can be thrown by some of them (although I have no idea, I suppose it based on that code).

That's why I have added the inner try/catch to release the lock. If the lock was released in the outer catch, it could be executed by exceptions thrown when no lock had to be released.
Comment 6 Daniel Calviño Sánchez 2012-06-20 18:08:39 UTC
Created attachment 71993 [details]
Patch for unittest.py and TestObject showing the bug

The bug, and the unit tests that show it, are still valid. I have updated the patch to apply cleanly using git (just removed two trailing whitespaces). It must be applied in "kdelibs" project.
Comment 7 Daniel Calviño Sánchez 2012-06-20 18:15:36 UTC
Created attachment 71994 [details]
Patch for pythonscript.cpp with a workaround to the bug

The proposed fix is still valid. I have updated the patch to apply cleanly using git (just changed the path to the modified file). It must be applied in "kross-interpreters" project (which is a subproject of "kdebindings").
Comment 8 Daniel Calviño Sánchez 2012-09-21 19:15:16 UTC
Git commit c17f808e3bac78d20f75b075ba3b6d9b5921e3df by Daniel Calviño Sánchez.
Committed on 21/09/2012 at 21:06.
Pushed by danxuliu into branch 'KDE/4.10'.

Add unit tests for crash in krosspython when a PyQt signal is connected to a C++ method that calls a function in the script.

M  +43   -0    kross/test/testobject.cpp
M  +10   -0    kross/test/testobject.h
M  +46   -0    kross/test/unittest.py

http://commits.kde.org/kdelibs/c17f808e3bac78d20f75b075ba3b6d9b5921e3df
Comment 9 Daniel Calviño Sánchez 2012-09-21 19:15:51 UTC
Git commit c74c9899d4c2e42ad17855bb4eb470ae188c2180 by Daniel Calviño Sánchez.
Committed on 21/09/2012 at 17:26.
Pushed by danxuliu into branch 'master'.

Fix crash when a PyQt signal is connected to a C++ method that calls a function in the script.

I would have prefered that someone with experience in the code had
reviewed the fix. Unfortunately, it seems that krosspython is currently
unmaintained, so I have decided to push the fix myself.

M  +31   -3    python/pythonscript.cpp

http://commits.kde.org/kross-interpreters/c74c9899d4c2e42ad17855bb4eb470ae188c2180