Fixed memory leak on callbacks used on signal connection.

Now using the 'destroyed()' signal the reference is cleaned after source
object destroyed.
This commit is contained in:
Renato Filho 2010-02-19 17:10:24 -03:00
commit 75b7afbd63
11 changed files with 212 additions and 84 deletions

View file

@ -58,6 +58,13 @@ static int registerString(const QByteArray& s, QList<QByteArray>* strings)
return idx;
}
static void clearItem(QLinkedList<QByteArray> &l, const QByteArray &value)
{
QLinkedList<QByteArray>::iterator i = qFind(l.begin(), l.end(), value);
if (i != l.end())
*i = QByteArray();
}
DynamicQMetaObject::DynamicQMetaObject(const char *className, const QMetaObject* metaObject)
{
d.superdata = metaObject;
@ -76,6 +83,15 @@ DynamicQMetaObject::~DynamicQMetaObject()
void DynamicQMetaObject::addSignal(const char* signal)
{
//search for a empty space
QByteArray blank;
QLinkedList<QByteArray>::iterator i = qFind(m_signals.begin(), m_signals.end(), blank);
if (i != m_signals.end()) {
*i = QByteArray(signal);
updateMetaObject();
return;
}
if (m_signals.size() >= MAX_SIGNALS_COUNT) {
qWarning() << "Fail to add dynamic signal to QObject. PySide support at most" << MAX_SIGNALS_COUNT << "dynamic signals.";
return;
@ -87,25 +103,34 @@ void DynamicQMetaObject::addSignal(const char* signal)
void DynamicQMetaObject::addSlot(const char* slot)
{
m_slots << QByteArray(slot);
//search for a empty space
QByteArray blank;
QLinkedList<QByteArray>::iterator i = qFind(m_slots.begin(), m_slots.end(), blank);
if (i != m_slots.end()) {
*i = QByteArray(slot);
} else {
m_slots << QByteArray(slot);
}
updateMetaObject();
}
void DynamicQMetaObject::removeSlot(uint index)
{
QMetaMethod m = method(index);
if (m_slots.removeAll(m.signature()))
if (m_slots.contains(m.signature())) {
clearItem(m_slots, m.signature());
updateMetaObject();
}
}
void DynamicQMetaObject::removeSignal(uint index)
{
//Current Qt implementation does not support runtime remove signal
/*
QMetaMethod m = method(index);
if (m_signals.removeAll(m.signature()))
if (m_signals.contains(m.signature())) {
clearItem(m_signals, m.signature());
updateMetaObject();
*/
}
}
void DynamicQMetaObject::updateMetaObject()

View file

@ -32,94 +32,125 @@
* 02110-1301 USA
*/
#include "globalreceiver.h"
#include <QMetaMethod>
#include <QDebug>
#include "signalmanager.h"
#include <QEvent>
#include <autodecref.h>
#include <gilstate.h>
#include "typeresolver.h"
#include "globalreceiver.h"
#include "typeresolver.h"
#include "signalmanager.h"
#include "weakref.h"
#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
namespace PySide
{
class DynamicSlotData
{
public:
DynamicSlotData(PyObject *callback);
void incRef();
void decRef();
DynamicSlotData(int id, PyObject* callback);
void addRef(const QObject* o);
void decRef(const QObject* o);
void clear();
bool hasRefTo(const QObject* o) const;
int refCount() const;
PyObject *callback() const;
int id() const;
PyObject* callback() const;
~DynamicSlotData();
private:
int m_refCount;
PyObject *m_callback;
int m_id;
PyObject* m_callback;
QSet<const QObject*> m_refs;
};
}
using namespace PySide;
DynamicSlotData::DynamicSlotData(PyObject *callback)
: m_refCount(0)
DynamicSlotData::DynamicSlotData(int id, PyObject* callback)
: m_id(id)
{
m_callback = callback;
Py_INCREF(callback);
}
void DynamicSlotData::incRef()
void DynamicSlotData::addRef(const QObject *o)
{
m_refCount++;
m_refs.insert(o);
}
void DynamicSlotData::decRef()
void DynamicSlotData::decRef(const QObject *o)
{
m_refCount--;
m_refs.remove(o);
}
int DynamicSlotData::refCount() const
{
return m_refCount;
return m_refs.size();
}
PyObject *DynamicSlotData::callback() const
PyObject* DynamicSlotData::callback() const
{
return m_callback;
}
int DynamicSlotData::id() const
{
return m_id;
}
bool DynamicSlotData::hasRefTo(const QObject *o) const
{
return m_refs.contains(o);
}
void DynamicSlotData::clear()
{
m_refs.clear();
}
DynamicSlotData::~DynamicSlotData()
{
Py_XDECREF(m_callback);
Py_DECREF(m_callback);
}
GlobalReceiver::GlobalReceiver()
: m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
{
//slot used to be notifyed of object destrouction
m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME);
}
GlobalReceiver::~GlobalReceiver()
{
foreach(DynamicSlotData* data, m_slotReceivers)
foreach(DynamicSlotData* data, m_slotReceivers) {
data->clear();
delete data;
}
}
void GlobalReceiver::connectNotify(int slotId)
{
if (m_slotReceivers.contains(slotId))
m_slotReceivers[slotId]->incRef();
}
void GlobalReceiver::disconnectNotify(int slotId)
void GlobalReceiver::connectNotify(QObject* source, int slotId)
{
if (m_slotReceivers.contains(slotId)) {
m_slotReceivers[slotId]->addRef(source);
}
}
void GlobalReceiver::disconnectNotify(QObject* source, int slotId)
{
if (m_slotReceivers.contains(slotId)) {
QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, "1"RECEIVER_DESTROYED_SLOT_NAME);
DynamicSlotData *data = m_slotReceivers[slotId];
data->decRef();
if (data->refCount() == 0)
data->decRef(source);
if (data->refCount() == 0) {
removeSlot(slotId);
}
}
}
@ -133,7 +164,7 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
m_metaObject.addSlot(slot);
int slotId = m_metaObject.indexOfSlot(slot);
if (!m_slotReceivers.contains(slotId)) {
m_slotReceivers[slotId] = new DynamicSlotData(callback);
m_slotReceivers[slotId] = new DynamicSlotData(slotId, callback);
}
bool isShortCircuit = true;
@ -143,6 +174,7 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
break;
}
}
if (isShortCircuit)
m_shortCircuitSlots << slotId;
@ -151,9 +183,23 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
void GlobalReceiver::removeSlot(int slotId)
{
delete m_slotReceivers.take(slotId);
m_metaObject.removeSlot(slotId);
m_shortCircuitSlots.remove(slotId);
if (m_slotReceivers.contains(slotId)) {
delete m_slotReceivers.take(slotId);
m_metaObject.removeSlot(slotId);
m_shortCircuitSlots.remove(slotId);
}
}
bool GlobalReceiver::hasConnectionWith(const QObject *object)
{
QHash<int, DynamicSlotData*>::iterator i = m_slotReceivers.begin();
while(i != m_slotReceivers.end()) {
if (i.value()->hasRefTo(object)) {
return true;
}
i++;
}
return false;
}
int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
@ -163,6 +209,20 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
QMetaMethod slot = m_metaObject.method(id);
Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
if (strcmp(slot.signature(), RECEIVER_DESTROYED_SLOT_NAME) == 0) {
QObject *arg = *(QObject**)args[1];
QHash<int, DynamicSlotData*>::iterator i = m_slotReceivers.begin();
while(i != m_slotReceivers.end()) {
if (i.value()->hasRefTo(arg)) {
disconnectNotify(arg, i.key());
break;
}
i++;
}
return -1;
}
DynamicSlotData* data = m_slotReceivers.value(id);
if (!data) {
qWarning() << "Unknown global slot, id:" << id;
@ -191,5 +251,6 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
qWarning() << "Error calling slot" << m_metaObject.method(id).signature();
else
Py_DECREF(retval);
return -1;
}

View file

@ -55,8 +55,10 @@ public:
const QMetaObject* metaObject() const;
void addSlot(const char* slot, PyObject* callback);
void removeSlot(int slotId);
void connectNotify(int slotId);
void disconnectNotify(int slotId);
void connectNotify(QObject* sender, int slotId);
void disconnectNotify(QObject* sender, int slotId);
bool hasConnectionWith(const QObject* object);
private:
DynamicQMetaObject m_metaObject;
QSet<int> m_shortCircuitSlots;

View file

@ -69,7 +69,12 @@ bool PySide::checkSignal(const char* signal)
static QString codeCallbackName(PyObject* callback, const QString& funcName)
{
return funcName+QString::number(quint64(callback), 16);
if (PyMethod_Check(callback)) {
PyObject *self = PyMethod_GET_SELF(callback);
PyObject *func = PyMethod_GET_FUNCTION(callback);
return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16);
} else
return funcName+QString::number(quint64(callback), 16);
}
QString PySide::getCallbackSignature(const char* signal, PyObject* callback, bool encodeName)
@ -185,14 +190,14 @@ QObject* SignalManager::globalReceiver()
return &m_d->m_globalReceiver;
}
void SignalManager::globalReceiverConnectNotify(int slotIndex)
void SignalManager::globalReceiverConnectNotify(QObject* source, int slotIndex)
{
m_d->m_globalReceiver.connectNotify(slotIndex);
m_d->m_globalReceiver.connectNotify(source, slotIndex);
}
void SignalManager::globalReceiverDisconnectNotify(int slotIndex)
void SignalManager::globalReceiverDisconnectNotify(QObject* source, int slotIndex)
{
m_d->m_globalReceiver.disconnectNotify(slotIndex);
m_d->m_globalReceiver.disconnectNotify(source, slotIndex);
}
void SignalManager::addGlobalSlot(const char* slot, PyObject* callback)
@ -234,7 +239,6 @@ bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* ar
return false;
signal++;
int signalIndex = source->metaObject()->indexOfSignal(signal);
if (signalIndex != -1) {
bool isShortCircuit;
@ -311,3 +315,8 @@ bool SignalManager::registerMetaMethod(QObject* source, const char* signature, Q
}
return true;
}
bool SignalManager::hasConnectionWith(const QObject *object)
{
return m_d->m_globalReceiver.hasConnectionWith(object);
}

View file

@ -61,11 +61,14 @@ public:
static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
void addGlobalSlot(const char* slot, PyObject* callback);
void globalReceiverConnectNotify(int slotIndex);
void globalReceiverDisconnectNotify(int slotIndex);
void globalReceiverConnectNotify(QObject *sender, int slotIndex);
void globalReceiverDisconnectNotify(QObject *sender, int slotIndex);
// Used to register a new signal/slot on QMetaobjc of source.
// Used to register a new signal/slot on QMetaobject of source.
static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type);
// Used to discovery if SignalManager was connected with object "destroyed()" signal.
bool hasConnectionWith(const QObject *object);
private:
struct SignalManagerPrivate;
SignalManagerPrivate* m_d;