Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Crash on lambda destructor (QString reference count)
QtWS25 Last Chance

Crash on lambda destructor (QString reference count)

Scheduled Pinned Locked Moved Unsolved General and Desktop
10 Posts 4 Posters 558 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    juangburgos
    wrote on 13 May 2021, 17:05 last edited by
    #1

    I am having a very strange crash when assiging nullptr to a lambda. It appears that before assiging the nullptr, the last lambda value is destroyed. The last lambda value captured a couple of QStrings and it seems that Qt crashes when is trying to decrease the counter of the QString instances.

    I don't know if I am doing something ilegal, or if this is a known issue, I could not find any info online. I attach some screenshots of the callstack.

    Environment:

    • Qt 5.6.3
    • CentOS 7
    • GCC 4.8.5

    This is where the nullptr is assigned (stack number 10):

    14e1efb1-818d-4c81-a18a-f4e5bba16be1-image.png

    This is where the last lambda value is destroyed and the crash follows (stack number 6):

    af1f1f3c-bbf7-454b-8853-75e22505fe64-image.png

    1 Reply Last reply
    0
    • V Offline
      V Offline
      VRonin
      wrote on 13 May 2021, 17:09 last edited by
      #2

      Can you show us the declaration of m_funcUpdatePartnerHost?

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      J 1 Reply Last reply 13 May 2021, 19:55
      0
      • V VRonin
        13 May 2021, 17:09

        Can you show us the declaration of m_funcUpdatePartnerHost?

        J Offline
        J Offline
        juangburgos
        wrote on 13 May 2021, 19:55 last edited by
        #3

        @VRonin sure, it is std::function<void(void)> m_funcUpdatePartnerHost

        K 1 Reply Last reply 14 May 2021, 06:06
        0
        • J juangburgos
          13 May 2021, 19:55

          @VRonin sure, it is std::function<void(void)> m_funcUpdatePartnerHost

          K Offline
          K Offline
          kshegunov
          Moderators
          wrote on 14 May 2021, 06:06 last edited by
          #4

          What is strSocketHost and strSocketPort? What is m_generalPartner and what does .fetch() do/return?

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          0
          • J Offline
            J Offline
            juangburgos
            wrote on 14 May 2021, 10:48 last edited by
            #5

            Sorry, strSocketHost and strSocketPort are QString instances (captured by the two lambdas shown in the screenshot.
            To explain further;

            • The .done([this, strSocketHost, strSocketPort]() { ... }) callback gets called after fetching some data from the network.

            • During the execution of the .done callback, the m_funcUpdatePartnerhost is assigned to [this, strSocketHost, strSocketPort]() { ... } as seen in the screenshot.

            • Later, when another event occurs, the m_funcUpdatePartnerhost is assigned to nullptr and this is when the crash occurs.

            As I understand, the lambdas capture the strSocketHost and strSocketPort (QString) by value, therefore Qt should increase the instance count of those strings, so they do not get deleted.

            Then when the m_funcUpdatePartnerhost is assigned to nullptr, the old lambda ( [this, strSocketHost, strSocketPort]() { ... }) get deleted, therefore the QString destructor reduces the instance count, and this is when the crash occurs.

            Hope I made the scenario clearer.

            K 1 Reply Last reply 14 May 2021, 11:25
            0
            • C Offline
              C Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on 14 May 2021, 11:08 last edited by
              #6

              Nice example on how to abuse lambdas so nobody understands what's going on there :)
              Use valgrind to see where the object get's deleted. Simplify the code until either we can compile and test it or it no longer crashes.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              0
              • J juangburgos
                14 May 2021, 10:48

                Sorry, strSocketHost and strSocketPort are QString instances (captured by the two lambdas shown in the screenshot.
                To explain further;

                • The .done([this, strSocketHost, strSocketPort]() { ... }) callback gets called after fetching some data from the network.

                • During the execution of the .done callback, the m_funcUpdatePartnerhost is assigned to [this, strSocketHost, strSocketPort]() { ... } as seen in the screenshot.

                • Later, when another event occurs, the m_funcUpdatePartnerhost is assigned to nullptr and this is when the crash occurs.

                As I understand, the lambdas capture the strSocketHost and strSocketPort (QString) by value, therefore Qt should increase the instance count of those strings, so they do not get deleted.

                Then when the m_funcUpdatePartnerhost is assigned to nullptr, the old lambda ( [this, strSocketHost, strSocketPort]() { ... }) get deleted, therefore the QString destructor reduces the instance count, and this is when the crash occurs.

                Hope I made the scenario clearer.

                K Offline
                K Offline
                kshegunov
                Moderators
                wrote on 14 May 2021, 11:25 last edited by
                #7

                @juangburgos said in Crash on lambda destructor (QString reference count):

                The .done(this, strSocketHost, strSocketPort { ... }) callback gets called after fetching some data from the network.

                .done() is a method, just as is .fetch(), I'll ask the same thing again, what do they return, at the very least provide the declarations of the relevant types.

                Later, when another event occurs, the m_funcUpdatePartnerhost is assigned to nullptr and this is when the crash occurs.

                The logic is wrong. You're introducing a state which is shared between all events, you're begging for trouble.

                As I understand, the lambdas capture the strSocketHost and strSocketPort (QString) by value, therefore Qt should increase the instance count of those strings, so they do not get deleted.

                This doesn't guard against you smashing the stack (or heap).


                Take note:

                @Christian-Ehrlicher said in Crash on lambda destructor (QString reference count):

                Nice example on how to abuse lambdas so nobody understands what's going on there :)
                Use valgrind to see where the object get's deleted. Simplify the code until either we can compile and test it or it no longer crashes.

                Read and abide by the Qt Code of Conduct

                1 Reply Last reply
                1
                • V Offline
                  V Offline
                  VRonin
                  wrote on 14 May 2021, 12:37 last edited by VRonin
                  #8

                  I tried this simplified case but can't reproduce the problem (in Qt 5.15). Can you:

                  • try the example below on your configuration and see if it crashes (Qt 5.6 is ancient and I can't fire it up easily on my end)?
                  • suggest how to make it more similar to your case to the point where we can test the crash?
                  #include <QCoreApplication>
                  #include <QDebug>
                  #include <QTimer>
                  class TestLambda : public QObject{
                  public:
                      explicit TestLambda(QObject* parent = nullptr)
                          :QObject(parent)
                      {
                          QString strSocketHost(QLatin1String("localhost"));
                          QString strSocketPort(QLatin1String("8080"));
                          m_generalPartner = [this,strSocketHost,strSocketPort](){
                              qDebug() << strSocketHost << strSocketPort;
                              m_funcUpdatePartnerHost= [this,strSocketHost,strSocketPort](){
                                  qDebug() << strSocketHost << strSocketPort;
                                  QTimer::singleShot(1000,this,&TestLambda::setFunctNull);
                              };
                              QTimer::singleShot(1000,this,m_funcUpdatePartnerHost);
                          };
                      }
                      void setFunctNull(){
                          qDebug("Setting to NULL");
                          m_funcUpdatePartnerHost=nullptr;
                          QTimer::singleShot(1000,qApp,&QCoreApplication::quit);
                      }
                      void start(){
                          m_generalPartner();
                      }
                      std::function<void(void)> m_funcUpdatePartnerHost;
                      std::function<void(void)> m_generalPartner;
                  };
                  
                  int main(int argc, char *argv[])
                  {
                      QCoreApplication app(argc,argv);
                      TestLambda testCrash;
                      testCrash.start();
                      return app.exec();
                  }
                  

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  K 1 Reply Last reply 14 May 2021, 15:08
                  3
                  • V VRonin
                    14 May 2021, 12:37

                    I tried this simplified case but can't reproduce the problem (in Qt 5.15). Can you:

                    • try the example below on your configuration and see if it crashes (Qt 5.6 is ancient and I can't fire it up easily on my end)?
                    • suggest how to make it more similar to your case to the point where we can test the crash?
                    #include <QCoreApplication>
                    #include <QDebug>
                    #include <QTimer>
                    class TestLambda : public QObject{
                    public:
                        explicit TestLambda(QObject* parent = nullptr)
                            :QObject(parent)
                        {
                            QString strSocketHost(QLatin1String("localhost"));
                            QString strSocketPort(QLatin1String("8080"));
                            m_generalPartner = [this,strSocketHost,strSocketPort](){
                                qDebug() << strSocketHost << strSocketPort;
                                m_funcUpdatePartnerHost= [this,strSocketHost,strSocketPort](){
                                    qDebug() << strSocketHost << strSocketPort;
                                    QTimer::singleShot(1000,this,&TestLambda::setFunctNull);
                                };
                                QTimer::singleShot(1000,this,m_funcUpdatePartnerHost);
                            };
                        }
                        void setFunctNull(){
                            qDebug("Setting to NULL");
                            m_funcUpdatePartnerHost=nullptr;
                            QTimer::singleShot(1000,qApp,&QCoreApplication::quit);
                        }
                        void start(){
                            m_generalPartner();
                        }
                        std::function<void(void)> m_funcUpdatePartnerHost;
                        std::function<void(void)> m_generalPartner;
                    };
                    
                    int main(int argc, char *argv[])
                    {
                        QCoreApplication app(argc,argv);
                        TestLambda testCrash;
                        testCrash.start();
                        return app.exec();
                    }
                    
                    K Offline
                    K Offline
                    kshegunov
                    Moderators
                    wrote on 14 May 2021, 15:08 last edited by
                    #9

                    @VRonin said in Crash on lambda destructor (QString reference count):

                    suggest how to make it more similar to your case to the point where we can test the crash?

                    Yes, please. I've encountered a bug in the code generation (the capture was generating wrong assembly) with MSVC (it was an old version and was fixed in the update), but never with gcc ...

                    Read and abide by the Qt Code of Conduct

                    J 1 Reply Last reply 14 May 2021, 17:44
                    0
                    • K kshegunov
                      14 May 2021, 15:08

                      @VRonin said in Crash on lambda destructor (QString reference count):

                      suggest how to make it more similar to your case to the point where we can test the crash?

                      Yes, please. I've encountered a bug in the code generation (the capture was generating wrong assembly) with MSVC (it was an old version and was fixed in the update), but never with gcc ...

                      J Offline
                      J Offline
                      juangburgos
                      wrote on 14 May 2021, 17:44 last edited by
                      #10

                      I wish I could reproduce it easily. I have indeed tried in multiple ways. This is a bug that happens rarely in production. The code shown is single threaded.

                      The stack you see is actually from a dump file. I am using QDeferred for the .done method.

                      Will keep trying to minimally reproduce.

                      1 Reply Last reply
                      0

                      5/10

                      14 May 2021, 10:48

                      • Login

                      • Login or register to search.
                      5 out of 10
                      • First post
                        5/10
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved