aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPankaj Pandey <pankaj86@gmail.com>2014-05-01 19:58:03 +0530
committerJohn Ehresman <jpe@wingware.com>2014-07-08 02:01:05 +0200
commit0c64d1b2c6e5e0951675ad9b22294db4a10741c7 (patch)
tree40a7680fd968bfb6c27f777b90d3a6aadd058db9
parent8dfeddb3a679d2f6fdb1ae01f8eee77c9bec7edd (diff)
PYSIDE-164: Fix possible deadlock on signal connect/emitps-4.8-head
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.cpp19
-rw-r--r--libpyside/pysidemetafunction.cpp2
-rw-r--r--tests/QtCore/CMakeLists.txt1
-rw-r--r--tests/QtCore/bug_PYSIDE-164.py39
4 files changed, 58 insertions, 3 deletions
diff --git a/PySide/QtCore/glue/qobject_connect.cpp b/PySide/QtCore/glue/qobject_connect.cpp
index 96bcfb227..f7bb5faaf 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 3a7d86ba7..98ce9366c 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 a8609de50..13567b351 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 000000000..53a1bd6c2
--- /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()