Skip to content

Commit 92471ff

Browse files
committed
compile thread support by default
added PythonQt::setEnableThreadSupport() to enable the thread support (disabled by default) added PythonQtSafeObjectPtr to allow safe GIL-aware storage in QVariant
1 parent 593ac63 commit 92471ff

11 files changed

+371
-70
lines changed

src/PythonQt.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,23 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
7272
{
7373
if (!_self) {
7474
_self = new PythonQt(flags, pythonQtModuleName);
75-
76-
PythonQt::priv()->setupSharedLibrarySuffixes();
7775

7876
_self->_p->_PythonQtObjectPtr_metaId = qRegisterMetaType<PythonQtObjectPtr>("PythonQtObjectPtr");
7977
PythonQtConv::registerMetaTypeToPythonConverter(_self->_p->_PythonQtObjectPtr_metaId, PythonQtConv::convertFromPythonQtObjectPtr);
8078
PythonQtConv::registerPythonToMetaTypeConverter(_self->_p->_PythonQtObjectPtr_metaId, PythonQtConv::convertToPythonQtObjectPtr);
79+
_self->_p->_PythonQtSafeObjectPtr_metaId = qRegisterMetaType<PythonQtSafeObjectPtr>("PythonQtSafeObjectPtr");
80+
PythonQtConv::registerMetaTypeToPythonConverter(_self->_p->_PythonQtSafeObjectPtr_metaId, PythonQtConv::convertFromPythonQtSafeObjectPtr);
81+
PythonQtConv::registerPythonToMetaTypeConverter(_self->_p->_PythonQtSafeObjectPtr_metaId, PythonQtConv::convertToPythonQtSafeObjectPtr);
82+
83+
PythonQtObjectPtr importlib;
84+
importlib.setNewRef(PyImport_ImportModule("importlib.machinery"));
85+
86+
if (importlib) {
87+
_self->_p->_pySourceFileLoader = importlib.getVariable("SourceFileLoader");
88+
_self->_p->_pySourcelessFileLoader = importlib.getVariable("SourcelessFileLoader");
89+
}
90+
91+
PythonQt::priv()->setupSharedLibrarySuffixes();
8192

8293
PythonQtMethodInfo::addParameterTypeAlias("QObjectList", "QList<QObject*>");
8394
qRegisterMetaType<QList<QObject*> >("QList<void*>");
@@ -302,6 +313,12 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
302313
Py_Initialize();
303314
}
304315

316+
#ifdef PYTHONQT_FULL_THREAD_SUPPORT
317+
if (!PyEval_ThreadsInitialized()) {
318+
PyEval_InitThreads();
319+
}
320+
#endif
321+
305322
// add our own python object types for qt object slots
306323
if (PyType_Ready(&PythonQtSlotFunction_Type) < 0) {
307324
std::cerr << "could not initialize PythonQtSlotFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
@@ -357,14 +374,6 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
357374
Py_INCREF(&PythonQtStdInRedirectType);
358375

359376
initPythonQtModule(flags & RedirectStdOut, pythonQtModuleName);
360-
361-
PythonQtObjectPtr importlib;
362-
importlib.setNewRef(PyImport_ImportModule("importlib.machinery"));
363-
364-
if (importlib) {
365-
_p->_pySourceFileLoader = importlib.getVariable("SourceFileLoader");
366-
_p->_pySourcelessFileLoader = importlib.getVariable("SourcelessFileLoader");
367-
}
368377
}
369378

370379
PythonQt::~PythonQt() {
@@ -1084,7 +1093,7 @@ QVariant PythonQt::getNativeVariable(PyObject* object, const QString& objectname
10841093
QVariant result;
10851094
PythonQtObjectPtr obj = lookupObject(object, objectname);
10861095
if (obj) {
1087-
result = QVariant::fromValue(obj);
1096+
result = obj.toVariant();
10881097
}
10891098
return result;
10901099
}
@@ -1708,6 +1717,14 @@ void PythonQt::setProfilingCallback(ProfilingCB* cb)
17081717
}
17091718

17101719

1720+
void PythonQt::setEnableThreadSupport(bool flag)
1721+
{
1722+
#ifdef PYTHONQT_FULL_THREAD_SUPPORT
1723+
PythonQtGILScope::setGILScopeEnabled(flag);
1724+
PythonQtSlotInfo::setGlobalShouldAllowThreads(flag);
1725+
#endif
1726+
}
1727+
17111728
static PyMethodDef PythonQtMethods[] = {
17121729
{NULL, NULL, 0, NULL}
17131730
};
@@ -2029,6 +2046,7 @@ bool PythonQtPrivate::isMethodDescriptor(PyObject* object) const
20292046

20302047
const QMetaObject* PythonQtPrivate::getDynamicMetaObject(PythonQtInstanceWrapper* wrapper, const QMetaObject* prototypeMetaObject)
20312048
{
2049+
PYTHONQT_GIL_SCOPE;
20322050
PythonQtDynamicClassInfo* info = wrapper->dynamicClassInfo();
20332051
if (info) {
20342052
if (!info->_dynamicMetaObject) {

src/PythonQt.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,13 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
601601
//! sets a callback that is called before and after function calls for profiling
602602
void setProfilingCallback(ProfilingCB* cb);
603603

604+
//! Enable GIL and thread state handling (turned off by default).
605+
//! If you want to use Python threading, you have to call this
606+
//! with true early in your main thread, before you launch
607+
//! any threads in Python. It can be called before or after
608+
//! PythonQt::init().
609+
static void setEnableThreadSupport(bool flag);
610+
604611
//@}
605612

606613
Q_SIGNALS:
@@ -665,6 +672,10 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
665672

666673
//! returns if the id is the id for PythonQtObjectPtr
667674
bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }
675+
//! returns if the id is the id for PythonQtSafeObjectPtr
676+
bool isPythonQtSafeObjectPtrMetaId(int id) { return _PythonQtSafeObjectPtr_metaId == id; }
677+
//! returns if the id is either PythonQtObjectPtr or PythonQtSafeObjectPtr
678+
bool isPythonQtAnyObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id || _PythonQtSafeObjectPtr_metaId == id; }
668679

669680
//! add the wrapper pointer (for reuse if the same obj appears while wrapper still exists)
670681
void addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper);
@@ -851,6 +862,7 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
851862

852863
int _initFlags;
853864
int _PythonQtObjectPtr_metaId;
865+
int _PythonQtSafeObjectPtr_metaId;
854866

855867
bool _hadError;
856868
bool _systemExitExceptionHandlerEnabled;

src/PythonQtConversion.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,9 @@ PyObject* PythonQtConv::convertQtValueToPythonInternal(int type, const void* dat
206206
return PythonQt::priv()->wrapQObject(*((QObject**)data));
207207

208208
default:
209-
if (PythonQt::priv()->isPythonQtObjectPtrMetaId(type)) {
210-
// special case, it is a PythonQtObjectPtr which contains a PyObject, take it directly:
209+
if (PythonQt::priv()->isPythonQtAnyObjectPtrMetaId(type)) {
210+
// special case, it is a PythonQtObjectPtr or PythonQtSafeObjectPtr which contains a PyObject, take it directly:
211+
// in case of PythonQtSafeObjectPtr, the cast is wrong but the ptr layout is identical, so that is ok.
211212
PyObject* o = ((PythonQtObjectPtr*)data)->object();
212213
Py_INCREF(o);
213214
return o;
@@ -388,7 +389,7 @@ void* PythonQtConv::ConvertPythonToQt(const PythonQtMethodInfo::ParameterInfo& i
388389

389390
if (PyObject_TypeCheck(obj, &PythonQtInstanceWrapper_Type) &&
390391
info.typeId != PythonQtMethodInfo::Variant &&
391-
!PythonQt::priv()->isPythonQtObjectPtrMetaId(info.typeId)) {
392+
!PythonQt::priv()->isPythonQtAnyObjectPtrMetaId(info.typeId)) {
392393
// if we have a Qt wrapper object and if we do not need a QVariant, we do the following:
393394
// (the Variant case is handled below in a switch)
394395

@@ -1037,11 +1038,8 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type)
10371038
} else if (PyList_Check(val) || PyTuple_Check(val) || PySequence_Check(val)) {
10381039
type = QVariant::List;
10391040
} else {
1040-
// this used to be:
1041-
// type = QVariant::String;
1042-
// but now we want to transport the Python Objects directly:
1043-
PythonQtObjectPtr o(val);
1044-
v = qVariantFromValue(o);
1041+
// transport the Python objects directly inside of QVariant:
1042+
v = PythonQtObjectPtr(val).toVariant();
10451043
return v;
10461044
}
10471045
}
@@ -1282,7 +1280,7 @@ PyObject* PythonQtConv::QVariantToPyObject(const QVariant& v)
12821280
return Py_None;
12831281
}
12841282
PyObject* obj = NULL;
1285-
if (v.userType() >= QMetaType::User && !PythonQt::priv()->isPythonQtObjectPtrMetaId(v.userType())) {
1283+
if (v.userType() >= QMetaType::User && !PythonQt::priv()->isPythonQtAnyObjectPtrMetaId(v.userType())) {
12861284
// try the slower way, which supports more conversions, e.g. QList<QObject*>
12871285
const PythonQtMethodInfo::ParameterInfo& info = PythonQtMethodInfo::getParameterInfoForMetaType(v.userType());
12881286
obj = ConvertQtValueToPython(info, v.constData());
@@ -1586,4 +1584,19 @@ PyObject* PythonQtConv::convertFromPythonQtObjectPtr( const void* /* PythonQtObj
15861584
// extra ref count, since we are supposed to return a newly refcounted object
15871585
Py_XINCREF(obj);
15881586
return obj;
1587+
}
1588+
1589+
bool PythonQtConv::convertToPythonQtSafeObjectPtr(PyObject* obj, void* /* PythonQtSafeObjectPtr* */ outPtr, int /*metaTypeId*/, bool /*strict*/)
1590+
{
1591+
// just store the PyObject inside of the smart ptr
1592+
*((PythonQtSafeObjectPtr*)outPtr) = obj;
1593+
return true;
1594+
}
1595+
1596+
PyObject* PythonQtConv::convertFromPythonQtSafeObjectPtr(const void* /* PythonQtSafeObjectPtr* */ inObject, int /*metaTypeId*/)
1597+
{
1598+
PyObject* obj = (*((const PythonQtSafeObjectPtr*)inObject)).object();
1599+
// extra ref count, since we are supposed to return a newly refcounted object
1600+
Py_XINCREF(obj);
1601+
return obj;
15891602
}

src/PythonQtConversion.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ class PYTHONQT_EXPORT PythonQtConv {
181181

182182
static bool convertToPythonQtObjectPtr(PyObject* obj, void* /* PythonQtObjectPtr* */ outPtr, int /*metaTypeId*/, bool /*strict*/);
183183
static PyObject* convertFromPythonQtObjectPtr(const void* /* PythonQtObjectPtr* */ inObject, int /*metaTypeId*/);
184+
static bool convertToPythonQtSafeObjectPtr(PyObject* obj, void* /* PythonQtObjectPtr* */ outPtr, int /*metaTypeId*/, bool /*strict*/);
185+
static PyObject* convertFromPythonQtSafeObjectPtr(const void* /* PythonQtObjectPtr* */ inObject, int /*metaTypeId*/);
184186
static bool convertToQListOfPythonQtObjectPtr(PyObject* obj, void* /* QList<PythonQtObjectPtr>* */ outList, int /*metaTypeId*/, bool /*strict*/);
185187
static PyObject* convertFromQListOfPythonQtObjectPtr(const void* /* QList<PythonQtObjectPtr>* */ inObject, int /*metaTypeId*/);
186188
static PyObject* convertFromStringRef(const void* inObject, int /*metaTypeId*/);

src/PythonQtImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args)
331331
// Pass the module name without the package prefix
332332
args.append(info.moduleName);
333333
// And the path where we know that the shared library is
334-
args.append(QVariant::fromValue(pathList));
334+
args.append(pathList.toLocalVariant());
335335
QVariant result = imp.call("find_module", args);
336336
if (result.isValid()) {
337337
// This will return a tuple with (file, pathname, description=(suffix,mode,type))

src/PythonQtMethodInfo.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ QHash<QByteArray, PythonQtMethodInfo*> PythonQtMethodInfo::_cachedSignatures;
4747
QHash<int, PythonQtMethodInfo::ParameterInfo> PythonQtMethodInfo::_cachedParameterInfos;
4848
QHash<QByteArray, QByteArray> PythonQtMethodInfo::_parameterNameAliases;
4949

50+
bool PythonQtSlotInfo::_globalShouldAllowThreads = false;
51+
5052
PythonQtMethodInfo::PythonQtMethodInfo(const QMetaMethod& meta, PythonQtClassInfo* classInfo)
5153
{
5254
#ifdef PYTHONQT_DEBUG
@@ -640,10 +642,15 @@ QByteArray PythonQtSlotInfo::getImplementingClassName() const
640642

641643
void PythonQtSlotInfo::invokeQtMethod(QObject* obj, PythonQtSlotInfo* slot, void** args)
642644
{
643-
if (slot->shouldAllowThreads()) {
645+
if (slot->shouldAllowThreads() && _globalShouldAllowThreads) {
644646
PYTHONQT_ALLOW_THREADS_SCOPE
645647
obj->qt_metacall(QMetaObject::InvokeMetaMethod, slot->slotIndex(), args);
646648
} else {
647649
obj->qt_metacall(QMetaObject::InvokeMetaMethod, slot->slotIndex(), args);
648650
}
649651
}
652+
653+
void PythonQtSlotInfo::setGlobalShouldAllowThreads(bool flag)
654+
{
655+
_globalShouldAllowThreads = flag;
656+
}

src/PythonQtMethodInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,21 @@ class PYTHONQT_EXPORT PythonQtSlotInfo : public PythonQtMethodInfo
231231
//! Invoke the given slot on obj, save/restore thread state if needed.
232232
static void invokeQtMethod(QObject* obj, PythonQtSlotInfo* slot, void** argList);
233233

234+
//! Sets if calling slots should release the GIL to allow other Python threads while being inside of C++
235+
static void setGlobalShouldAllowThreads(bool flag);
236+
237+
//! Returns if calling slots should release the GIL to allow Python threads while being inside of C++
238+
static bool getGlobalShouldAllowThreads();
239+
234240
private:
235241
int _slotIndex;
236242
PythonQtSlotInfo* _next;
237243
QObject* _decorator;
238244
Type _type;
239245
QMetaMethod _meta;
240246
int _upcastingOffset;
247+
248+
static bool _globalShouldAllowThreads;
241249
};
242250

243251

src/PythonQtObjectPtr.cpp

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,6 @@
4040
//----------------------------------------------------------------------------------
4141

4242
#include <PythonQt.h>
43-
44-
PythonQtObjectPtr::PythonQtObjectPtr(PyObject* o)
45-
{
46-
_object = o;
47-
if (o) Py_INCREF(_object);
48-
}
49-
50-
PythonQtObjectPtr::~PythonQtObjectPtr()
51-
{
52-
if (_object) Py_DECREF(_object);
53-
}
54-
55-
void PythonQtObjectPtr::setNewRef(PyObject* o)
56-
{
57-
if (o != _object) {
58-
if (_object) Py_DECREF(_object);
59-
_object = o;
60-
}
61-
}
6243

6344
QVariant PythonQtObjectPtr::evalScript(const QString& script, int start)
6445
{
@@ -106,18 +87,62 @@ QVariant PythonQtObjectPtr::call(const QVariantList& args, const QVariantMap& kw
10687
return PythonQt::self()->call(_object, args, kwargs);
10788
}
10889

90+
PythonQtObjectPtr::PythonQtObjectPtr(PyObject* o)
91+
{
92+
_object = o;
93+
if (o) Py_INCREF(_object);
94+
}
95+
96+
PythonQtObjectPtr::PythonQtObjectPtr(PythonQtSafeObjectPtr &&p) :_object(p.takeObject())
97+
{
98+
}
99+
100+
PythonQtObjectPtr::~PythonQtObjectPtr()
101+
{
102+
if (_object) Py_DECREF(_object);
103+
}
104+
105+
void PythonQtObjectPtr::setNewRef(PyObject* o)
106+
{
107+
if (o != _object) {
108+
if (_object) Py_DECREF(_object);
109+
_object = o;
110+
}
111+
}
112+
109113
bool PythonQtObjectPtr::fromVariant(const QVariant& variant)
110114
{
111115
if (!variant.isNull()) {
112-
setObject(qvariant_cast<PythonQtObjectPtr>(variant));
113-
return true;
116+
PyObject* object = NULL;
117+
if (PythonQt::priv()->isPythonQtSafeObjectPtrMetaId(variant.userType())) {
118+
object = (*((const PythonQtSafeObjectPtr*)variant.constData())).object();
119+
} else if (PythonQt::priv()->isPythonQtObjectPtrMetaId(variant.userType())) {
120+
object = (*((const PythonQtObjectPtr*)variant.constData())).object();
121+
}
122+
setObject(object);
123+
return true;
114124
}
115125
else {
116-
setObject(0);
117-
return false;
126+
setObject(NULL);
127+
return false;
118128
}
119129
}
120130

131+
QVariant PythonQtObjectPtr::toVariant()
132+
{
133+
return QVariant::fromValue(PythonQtSafeObjectPtr(*this));
134+
}
135+
136+
137+
PythonQtObjectPtr & PythonQtObjectPtr::operator=(PythonQtSafeObjectPtr &&p)
138+
{
139+
if (_object) {
140+
setObject(NULL);
141+
}
142+
_object = p.takeObject();
143+
return *this;
144+
}
145+
121146
void PythonQtObjectPtr::setObject(PyObject* o)
122147
{
123148
if (o != _object) {
@@ -126,3 +151,52 @@ void PythonQtObjectPtr::setObject(PyObject* o)
126151
if (_object) Py_INCREF(_object);
127152
}
128153
}
154+
155+
//------------------------------------------------------------------------------
156+
157+
PythonQtSafeObjectPtr::PythonQtSafeObjectPtr(PyObject* o)
158+
{
159+
_object = o;
160+
if (o) {
161+
PYTHONQT_GIL_SCOPE
162+
Py_INCREF(_object);
163+
}
164+
}
165+
166+
PythonQtSafeObjectPtr::~PythonQtSafeObjectPtr()
167+
{
168+
if (_object) {
169+
PYTHONQT_GIL_SCOPE
170+
Py_DECREF(_object);
171+
}
172+
}
173+
174+
void PythonQtSafeObjectPtr::setObject(PyObject* o)
175+
{
176+
if (o != _object) {
177+
PYTHONQT_GIL_SCOPE
178+
if (_object) Py_DECREF(_object);
179+
_object = o;
180+
if (_object) Py_INCREF(_object);
181+
}
182+
}
183+
184+
void PythonQtSafeObjectPtr::setObjectUnsafe(PyObject* o)
185+
{
186+
if (o != _object) {
187+
if (_object) Py_DECREF(_object);
188+
_object = o;
189+
if (_object) Py_INCREF(_object);
190+
}
191+
}
192+
193+
194+
//--------------------------------------------------------------------------
195+
196+
// we do this here to allow toLocalVariant() to create a QVariant
197+
Q_DECLARE_METATYPE(PythonQtObjectPtr)
198+
199+
QVariant PythonQtObjectPtr::toLocalVariant()
200+
{
201+
return QVariant::fromValue(PythonQtSafeObjectPtr(*this));
202+
}

0 commit comments

Comments
 (0)