diff options
author | Renato Filho <renato.filho@openbossa.org> | 2011-08-18 17:51:13 -0300 |
---|---|---|
committer | Hugo Parente Lima <hugo.pl@gmail.com> | 2012-03-08 16:54:46 -0300 |
commit | 7b2c4242ad1d86bcc2da43a8482dbd39b3ac988e (patch) | |
tree | affbe074d73898afb10de5ae4685d698c5fb2f70 /libpyside/globalreceiverv2.cpp | |
parent | d00af325c696acc390dd3565be82cd22b3b94658 (diff) |
Implemented GlobalReceiverV2.
A new implementation of GlobalRecever that optmize the signal
connection.
Fixes bug #928.
Reviewer: Marcelo Lira <marcelo.lira@openbossa.org>
Luciano Wolf <luciano.wolf@openbossa.org>
Diffstat (limited to 'libpyside/globalreceiverv2.cpp')
-rw-r--r-- | libpyside/globalreceiverv2.cpp | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/libpyside/globalreceiverv2.cpp b/libpyside/globalreceiverv2.cpp new file mode 100644 index 000000000..c31a9d2f6 --- /dev/null +++ b/libpyside/globalreceiverv2.cpp @@ -0,0 +1,308 @@ +/* +* This file is part of the PySide project. +* +* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). +* +* Contact: PySide team <contact@pyside.org> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "globalreceiverv2.h" +#include "dynamicqmetaobject_p.h" +#include "pysideweakref.h" + +#include <QMetaMethod> +#include <QDebug> +#include <QEvent> +#include <QLinkedList> +#include <autodecref.h> +#include <gilstate.h> + +#include "typeresolver.h" +#include "signalmanager.h" + +#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" + +namespace +{ + static int DESTROY_SIGNAL_ID = 0; + static int DESTROY_SLOT_ID = 0; +} + +namespace PySide +{ +class DynamicSlotDataV2 +{ + public: + DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent); + ~DynamicSlotDataV2(); + + int addSlot(const char* signature); + int id(const char* signature) const; + PyObject* call(PyObject* args); + QByteArray hash() const; + void notify(); + + static void onCallbackDestroyed(void* data); + static QByteArray hash(PyObject *callback); + + + private: + bool m_isMethod; + PyObject* m_callback; + PyObject* m_pythonSelf; + PyObject* m_pyClass; + PyObject* m_weakRef; + QMap<QByteArray, int> m_signatures; + GlobalReceiverV2* m_parent; + QByteArray m_hash; +}; + +} + +using namespace PySide; + +DynamicSlotDataV2::DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent) + : m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent) +{ + m_isMethod = PyMethod_Check(callback); + if (m_isMethod) { + //Can not store calback pointe because this will be destroyed at the end of the scope + //To avoid increment intance reference keep the callback information + m_callback = PyMethod_GET_FUNCTION(callback); + m_pyClass = PyMethod_GET_CLASS(callback); + m_pythonSelf = PyMethod_GET_SELF(callback); + + //monitor class from method lifetime + m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this); + + m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback)) + + QByteArray::number((qlonglong)PyObject_Hash(m_pythonSelf)); + + } else { + m_callback = callback; + Py_INCREF(m_callback); + + m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback)); + } +} + +QByteArray DynamicSlotDataV2::hash() const +{ + return m_hash; +} + +QByteArray DynamicSlotDataV2::hash(PyObject* callback) +{ + Shiboken::GilState gil; + if (PyMethod_Check(callback)) + return QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_FUNCTION(callback))) + + QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_SELF(callback))); + else + return QByteArray::number((qlonglong)PyObject_Hash(callback)); +} + +PyObject* DynamicSlotDataV2::call(PyObject* args) +{ + PyObject* callback = m_callback; + + //create a callback based on method data + if (m_isMethod) + callback = PyMethod_New(m_callback, m_pythonSelf, m_pyClass); + + PyObject* result = PyObject_CallObject(callback, args); + + if (m_isMethod) + Py_DECREF(callback); + + return result; +} + +int DynamicSlotDataV2::id(const char* signature) const +{ + if (m_signatures.contains(signature)) + return m_signatures[signature]; + return -1; +} + +int DynamicSlotDataV2::addSlot(const char* signature) +{ + int index = id(signature); + if (index == -1) { + DynamicQMetaObject *dmo = const_cast<DynamicQMetaObject*>(reinterpret_cast<const DynamicQMetaObject*>(m_parent->metaObject())); + index = m_signatures[signature] = dmo->addSlot(signature); + } + return index; +} + +void DynamicSlotDataV2::onCallbackDestroyed(void *data) +{ + DynamicSlotDataV2* self = reinterpret_cast<DynamicSlotDataV2*>(data); + self->m_weakRef = 0; + delete self->m_parent; +} + +DynamicSlotDataV2::~DynamicSlotDataV2() +{ + Shiboken::GilState gil; + + Py_XDECREF(m_weakRef); + m_weakRef = 0; + + if (!m_isMethod) + Py_DECREF(m_callback); +} + +GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) + : QObject(0), m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject), m_sharedMap(map) +{ + m_data = new DynamicSlotDataV2(callback, this); + m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); + m_metaObject.update(); + m_refs.append(NULL); + + + if (DESTROY_SIGNAL_ID == 0) + DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); + + if (DESTROY_SLOT_ID == 0) + DESTROY_SLOT_ID = m_metaObject.indexOfSlot(RECEIVER_DESTROYED_SLOT_NAME); + + +} + +GlobalReceiverV2::~GlobalReceiverV2() +{ + m_refs.clear(); + //Remove itself from map + m_sharedMap->remove(m_data->hash()); + delete m_data; +} + +int GlobalReceiverV2::addSlot(const char* signature) +{ + return m_data->addSlot(signature); +} + +void GlobalReceiverV2::incRef(const QObject* link) +{ + if (link) { + if (!m_refs.contains(link)) { + if (QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID)) + m_refs.append(link); + else + Q_ASSERT(false); + } else { + m_refs.append(link); + } + } else { + m_refs.append(NULL); + } +} + +void GlobalReceiverV2::decRef(const QObject* link) +{ + if (m_refs.size() <= 0) + return; + + + m_refs.removeOne(link); + if (link) { + if (!m_refs.contains(link)) { + bool result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + Q_ASSERT(result); + if (!result) + return; + } + } + + if (m_refs.size() == 0) + delete this; + +} + +int GlobalReceiverV2::refCount(const QObject* link) const +{ + if (link) + return m_refs.count(link); + + return m_refs.size(); +} + +void GlobalReceiverV2::notify() +{ + QSet<const QObject*> objs = QSet<const QObject*>::fromList(m_refs); + foreach(const QObject* o, objs) { + QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); + } +} + +QByteArray GlobalReceiverV2::hash() const +{ + return m_data->hash(); +} + +QByteArray GlobalReceiverV2::hash(PyObject* callback) +{ + return DynamicSlotDataV2::hash(callback); +} + +const QMetaObject* GlobalReceiverV2::metaObject() const +{ + return m_metaObject.update(); +} + +int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); + + QMetaMethod slot = metaObject()->method(id); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + + if (id == DESTROY_SLOT_ID) { + if (m_refs.size() == 0) + return -1; + QObject *obj = *(QObject**)args[1]; + incRef(); //keep the object live (safe ref) + m_refs.removeAll(obj); // remove all refs to this object + decRef(); //remove the safe ref + } else { + Shiboken::GilState gil; + PyObject* retval = 0; + + bool isShortCurt = (strstr(slot.signature(), "(") == 0); + if (isShortCurt) { + retval = m_data->call(reinterpret_cast<PyObject*>(args[1])); + } else { + QList<QByteArray> paramTypes = slot.parameterTypes(); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); + for (int i = 0, max = paramTypes.count(); i < max; ++i) { + PyObject* arg = Shiboken::TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); // Do not increment the reference + PyTuple_SET_ITEM(preparedArgs.object(), i, arg); + } + retval = m_data->call(preparedArgs); + } + + if (!retval) + PyErr_Print(); + else + Py_DECREF(retval); + } + + return -1; +} |