24

The new Qt5 signals and slots syntax allows us to connect signals not only to slots, but also to plain old functions and functors/lambdas. Now the problem is, that lambdas are essentialy objects with () operator, and when you connect signals to them, they get copied somewhere in qt internal classes. And, when you disconnect the signal from that functor, it stays in qt internals. I fail to understand, is that a normal behaviour? Or maybe there is a way to destroy those functional objects after disconnection?

Here's an example:

//example

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTimer* timer = new QTimer();

    QSharedPointer<QMetaObject::Connection> connection(new QMetaObject::Connection());

    //functor is created and gets copied inside qt internals, connection variable is captured
    //inside the functor

    *connection.data() = QObject::connect(timer, &QTimer::timeout, [=]
    {
        qDebug() << "disconnected";
        QObject::disconnect(*connection.data());
    });

    timer->start(10000);

    return a.exec();
}

//example

Now when i look at strong reference count of connection variable after the slot disconnection, it stays 2, which means that the functor object itself is still alive and well, though it is of no use to me now. Do I miss something?

Sigil
  • 243
  • 2
  • 6
  • What happens when you destroy the timer? – Reuben Morais Dec 13 '12 at 05:44
  • Had that thought too, but the reference count still stays 2 =/ – Sigil Dec 13 '12 at 12:38
  • The only way it goes to 1 if i don't store the connection variable in that ' *connection.data() = 'part and delete the timer. Which is still very strange. – Sigil Dec 13 '12 at 12:43
  • It's been a while since I used Qt, but can't you reconnect using the same parameters using the returned `QMetaObject::Connection`? That would explain why it's keeping a reference to the function. – Reuben Morais Dec 13 '12 at 17:56
  • 1
    I don't think there's a reconnect method. From the source code, disconnect() set the receiver to 0. So there's no way to restore the connection. I think OP did find a memory leak. Please file a bug report. – Stephen Chu Dec 13 '12 at 18:57

1 Answers1

10

The example is over-engineered (why using a QSharedPointer? why capturing it by value?). But indeed Qt is leaking the functor object.

The point is that the internal connection list is simply marked as dirty, and not cleared until either the sender is deleted or a new signal is connected (see the usages of cleanConnectionLists).

I pushed a couple of patches that should fix this behaviour: https://codereview.qt-project.org/#change,42976 and 42979

peppe
  • 21,934
  • 4
  • 55
  • 70
  • 2
    Well yeah, I know that it may look a bit over-engineered, but I needed that shared pointer just to be able to easily monitor the number of references to it, to see if the functor object still alive, and I do have a real project where similar logic is not inside the main function, so I cannot just capture connection by reference there. – Sigil Dec 21 '12 at 18:34