diff options
author | Pankaj Pandey <pankaj86@gmail.com> | 2014-05-01 19:58:03 +0530 |
---|---|---|
committer | John Ehresman <jpe@wingware.com> | 2014-07-08 02:01:05 +0200 |
commit | 0c64d1b2c6e5e0951675ad9b22294db4a10741c7 (patch) | |
tree | 40a7680fd968bfb6c27f777b90d3a6aadd058db9 | |
parent | 8dfeddb3a679d2f6fdb1ae01f8eee77c9bec7edd (diff) |
PYSIDE-164: Fix possible deadlock on signal connect/emit4.8
Signal connect/emit acquire a lock on the QObject, and can
happen from python code (which has acquired the GIL) or
internal QtCode (without acquiring the GIL).
So we always need to release the GIL to prevent out-of-order
acquisition of the locks causing deadlock.
Change-Id: I1cf47a73c2b60627e322d8ef3fa4c3efdebd4c02
Reviewed-by: John Ehresman <jpe@wingware.com>
-rw-r--r-- | PySide/QtCore/glue/qobject_connect.cpp | 19 | ||||
-rw-r--r-- | libpyside/pysidemetafunction.cpp | 2 | ||||
-rw-r--r-- | tests/QtCore/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/QtCore/bug_PYSIDE-164.py | 39 |
4 files changed, 58 insertions, 3 deletions
diff --git a/PySide/QtCore/glue/qobject_connect.cpp b/PySide/QtCore/glue/qobject_connect.cpp index 96bcfb22..f7bb5faa 100644 --- a/PySide/QtCore/glue/qobject_connect.cpp +++ b/PySide/QtCore/glue/qobject_connect.cpp @@ -61,7 +61,11 @@ static bool qobjectConnect(QObject* source, const char* signal, QObject* receive bool isSignal = PySide::Signal::isQtSignal(slot); slot++; PySide::SignalManager::registerMetaMethod(receiver, slot, isSignal ? QMetaMethod::Signal : QMetaMethod::Slot); - return QObject::connect(source, signal - 1, receiver, slot - 1, type); + bool connected; + Py_BEGIN_ALLOW_THREADS + connected = QObject::connect(source, signal - 1, receiver, slot - 1, type); + Py_END_ALLOW_THREADS + return connected; } static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType type) @@ -109,7 +113,11 @@ static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject return false; } } - if (QMetaObject::connect(source, signalIndex, receiver, slotIndex, type)) { + bool connected; + Py_BEGIN_ALLOW_THREADS + connected = QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); + Py_END_ALLOW_THREADS + if (connected) { if (usingGlobalReceiver) signalManager.notifyGlobalReceiver(receiver); #ifndef AVOID_PROTECTED_HACK @@ -151,7 +159,12 @@ static bool qobjectDisconnectCallback(QObject* source, const char* signal, PyObj slotIndex = metaObject->indexOfSlot(callbackSig); - if (QMetaObject::disconnectOne(source, signalIndex, receiver, slotIndex)) { + bool disconnected; + Py_BEGIN_ALLOW_THREADS + disconnected = QMetaObject::disconnectOne(source, signalIndex, receiver, slotIndex); + Py_END_ALLOW_THREADS + + if (disconnected) { if (usingGlobalReceiver) signalManager.releaseGlobalReceiver(source, receiver); diff --git a/libpyside/pysidemetafunction.cpp b/libpyside/pysidemetafunction.cpp index 3a7d86ba..98ce9366 100644 --- a/libpyside/pysidemetafunction.cpp +++ b/libpyside/pysidemetafunction.cpp @@ -198,7 +198,9 @@ bool call(QObject* self, int methodIndex, PyObject* args, PyObject** retVal) bool ok = i == numArgs; if (ok) { + Py_BEGIN_ALLOW_THREADS QMetaObject::metacall(self, QMetaObject::InvokeMetaMethod, method.methodIndex(), methArgs); + Py_END_ALLOW_THREADS if (retVal) { if (methArgs[0]) { diff --git a/tests/QtCore/CMakeLists.txt b/tests/QtCore/CMakeLists.txt index a8609de5..13567b35 100644 --- a/tests/QtCore/CMakeLists.txt +++ b/tests/QtCore/CMakeLists.txt @@ -28,6 +28,7 @@ PYSIDE_TEST(bug_1031.py) PYSIDE_TEST(bug_1063.py) PYSIDE_TEST(bug_1069.py) PYSIDE_TEST(bug_PYSIDE-42.py) +PYSIDE_TEST(bug_PYSIDE-164.py) PYSIDE_TEST(blocking_signals_test.py) PYSIDE_TEST(classinfo_test.py) PYSIDE_TEST(child_event_test.py) diff --git a/tests/QtCore/bug_PYSIDE-164.py b/tests/QtCore/bug_PYSIDE-164.py new file mode 100644 index 00000000..53a1bd6c --- /dev/null +++ b/tests/QtCore/bug_PYSIDE-164.py @@ -0,0 +1,39 @@ +from PySide.QtCore import QCoreApplication, QEventLoop, QObject, Qt, QThread, QTimer, SIGNAL +import unittest + + +class Emitter(QThread): + def __init__(self): + QThread.__init__(self) + + def run(self): + print "Before emit." + self.emit(SIGNAL("signal(int)"), 0) + print "After emit." + +class Receiver(QObject): + def __init__(self, eventloop): + QObject.__init__(self) + self.eventloop = eventloop + + def receive(self, number): + print "Received number: %d" % number + self.eventloop.exit(0) + + +class TestBugPYSIDE164(unittest.TestCase): + + def testBlockingSignal(self): + app = QCoreApplication.instance() or QCoreApplication([]) + eventloop = QEventLoop() + emitter = Emitter() + receiver = Receiver(eventloop) + emitter.connect(emitter, SIGNAL("signal(int)"), + receiver.receive, Qt.BlockingQueuedConnection) + emitter.start() + retval = eventloop.exec_() + emitter.wait() + self.assertEqual(retval, 0) + +if __name__ == '__main__': + unittest.main() |