PDA

View Full Version : QObject Automatic Signal Disconnection



_M_chipz
15th April 2012, 11:52
Hello all, I'm unsure if this is the appropriate location for this suggestion.
I've been heavily using the QNetworkAccessManager recently for applications which are designed to run for months at a time, I noticed a memory issue which can't be properly classified as a leak.

This small block demonstrates the issue (it could be connected to anything)



QNetworkReply* reply = network_access_manager.get(request);

QEventLoop e_l;
connect(reply, SIGNAL(finished()),
&e_l , SLOT(quit()));
e_l.exec();

/* ... */

reply->deleteLater();


After execution of this code, all allocated memory is freed...however somewhere in the QObject world there's reference to reply being connected to e_l. This remains true even if both variables are freed, at which point there's no way to remove the reference (as far as I'm aware) and so it's essentially lost memory. My very rough test found using Qt 4.8.0 Linux x86_64 that .543K was lost per connection which was not disconnected. This behaviour was unexpected to me (even if I am in the wrong) and I'd predict it to cause unexpected leaks for anyone developing with the network utilities. Is there no reason the d-tor of the freed cannot inform the paired object so this reference can be removed?

wysota
15th April 2012, 12:43
however somewhere in the QObject world there's reference to reply being connected to e_l. This remains true even if both variables are freed
No, that's not true. There is a piece of code in QObject destructor that breaks all signal-slot connections regarding the object being destroyed.


// disconnect all receivers
if (d->connectionLists) {
++d->connectionLists->inUse;
int connectionListsCount = d->connectionLists->count();
for (int signal = -1; signal < connectionListsCount; ++signal) {
QObjectPrivate::ConnectionList &connectionList =
(*d->connectionLists)[signal];

while (QObjectPrivate::Connection *c = connectionList.first) {
if (!c->receiver) {
connectionList.first = c->nextConnectionList;
delete c;
continue;
}

QMutex *m = signalSlotLock(c->receiver);
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);

if (c->receiver) {
*c->prev = c->next;
if (c->next) c->next->prev = c->prev;
}
if (needToUnlock)
m->unlockInline();

connectionList.first = c->nextConnectionList;
delete c;
}
}

if (!--d->connectionLists->inUse) {
delete d->connectionLists;
} else {
d->connectionLists->orphaned = true;
}
d->connectionLists = 0;
}

// disconnect all senders
QObjectPrivate::Connection *node = d->senders;
while (node) {
QObject *sender = node->sender;
QMutex *m = signalSlotLock(sender);
node->prev = &node;
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
//the node has maybe been removed while the mutex was unlocked in relock?
if (!node || node->sender != sender) {
m->unlockInline();
continue;
}
node->receiver = 0;
QObjectConnectionListVector *senderLists = sender->d_func()->connectionLists;
if (senderLists)
senderLists->dirty = true;

node = node->next;
if (needToUnlock)
m->unlockInline();
}

My very rough test found using Qt 4.8.0 Linux x86_64 that .543K was lost per connection which was not disconnected.
How exactly did you test it?

_M_chipz
15th April 2012, 14:31
Hello wysota. Apologies, in my test case I was using deleteLater() in a tight loop and using pmap to track memory. It caused more memory to be mapped to the process which tricked me into seeing leak-like behaviour. Changing the test methodology shows there is in fact no issue.

This post can be closed (If such things are done here).