[Development] leak in QMetaObject?

Olivier Goffart olivier at woboq.com
Tue Jul 19 18:06:56 CEST 2016


On Donnerstag, 14. Juli 2016 17:45:58 CEST Thomas Senyk wrote:
> On 14.07.2016 17:18, André Somers wrote:
> > Op 14/07/2016 om 17:10 schreef Olivier Goffart:
> >> On Donnerstag, 14. Juli 2016 16:33:26 CEST Thomas Senyk wrote:
> >>> Hi,
> >>> 
> >>> I lately wanted to validate that a connecting&disconnecting to a lambda
> >>> will not leak the lambda object .. and found that more then that is
> >>> leaked.
> >>> 
> >>> Here is my example: http://paste.ofcode.org/36dmWCT8ddF6Zaqb9csKZNv
> >>> 
> >>> After the 10million connections are executed and disconnected (all of
> >>> them a successful), I assume a drop in memory consumption and more
> >>> importantly a drop of cpu load to close to 0%.
> >>> 
> >>> I see neither one nor the other. I see continues 100% consume with no
> >>> memory consumption drop what so ever.
> >>> 
> >>> I did a quick peak into QMetaObject::activate and it seems no clean up
> >>> happened .. the list returned in
> >>> 
> >>>    list = &connectionLists->at(signal_index);
> >>>    (line #3660 in 5.6 checkout from today)
> >>> 
> >>> seems rather long! (10million?)
> >>> 
> >>> However all "if (!c->receiver)" shows no receiver and therefor will
> >>> continue right away .. at least
> >>> 
> >>> Is this expected? A bug? ... I'm happy to fill a bug report, I just
> >>> wanted to get some feedback, maybe I'm just doing something wrong
> >> 
> >> The lambda object and its capture is destroyed by the call to
> >> 
> >>    slotObj->destroyIfLastRef()
> >> 
> >> The internal data structre (QObjectPrivate::Connection) is deleted by
> >> QObjectPrivate::cleanConnectionLists which is actually only called when
> >> a new connection is made to this object.
> >> 
> >> I don't think there is a leak, but it is true that some memory is only
> >> free'ed
> >> when you add new connection or that the object is destroyed. (but the
> >> lambda
> >> object is destroyed right after the disconnection)
> 
> Then this:
> http://paste.ofcode.org/37rxyitynhEqi5gira88hR9
> 
> should "fix" it? .. but I still see 100% cpu and same memory consumption?

As thiago says, your thing still does not work because when you connect from a 
signal connected to the same object, the list is inUse and will not be 
cleaned.

for (int i = 1; i <= 10000000; i++) {
 // ... your code 
}
QTimer::singleShot(200, // so it's after the last disconnect
  [] { QObject::disconnect(QObject::connect(t, &QTimer::timeout, []{})); });

So the connect happens after everything was disconnected.

In practice you probably don't need to use this workaround, unless you always 
add new connection from a slot connected to a signal of the object you 
continue to connect and disconnect to. Is that what you are doing?

It is true that we could consider trying to clean the connection list after 
each signal emission if it is dirty.
We don't want to do it after each disconnect because this can turn a O(n) 
algorithm into a O(n^2)   (cleaning the list is O(n), if we have to do it for 
every disconnect, that n*O(n))


> >>> Some background:
> >>> I wanted to check if a single-shot-connection (connection to a signal
> >>> and disconnect at first execute, using a lambda as slot) would be a
> >>> feasible thing to do.
> >> 
> >> You can do that already:
> >>   QTimer::sigleShot(100, [] { qDebug() << "hello"; });
> > 
> > That's not the same thing.
> > This looks like my earlier suggestion
> > https://bugreports.qt.io/browse/QTBUG-44219
> 
> +1, this is similar but not the same,

Ah right, I got confused because you use the term "single shot" and your 
testcase was using a QTimer.

Yes that's a valid feature request. But I have no plan to spend time 
implementing this myself anytime soon.

-- 
Olivier

Woboq - Qt services and support - https://woboq.com - https://code.woboq.org




More information about the Development mailing list