Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-105182

Access violation / infinite loop in QFutureInterfaceBase::isChainCanceled()

    XMLWordPrintable

Details

    • Linux/X11, Windows
    • 8
    • 24dedaeaa1 (qt/qtbase/dev) ee349ffba0 (qt/qtbase/6.4) ee349ffba0 (qt/tqtc-qtbase/6.4) 24dedaeaa1 (qt/tqtc-qtbase/dev) 0006a401a4 (qt/tqtc-qtbase/6.2) 018d013181 (qt/qtbase/6.3) 018d013181 (qt/tqtc-qtbase/6.3)
    • Foundation Sprint 65, Foundation Sprint 66

    Description

      I was able to create a mini application that shows the behavior. I've isolated the behavior that causes problems to it's bare minimums that kind of resembles the structure how I use QFuture in my application. 

      Original description below.

      #include <QGuiApplication>
      #include <QTimer>
      #include <QPromise>
      #include <QFuture>
      
      QFuture<bool> returnTrueAsync()
      {
          QTimer* timer = new QTimer();
      
          auto future = QtFuture::connect(timer, &QTimer::timeout)
                  .then([timer](){
                      timer->deleteLater();
                      return true;
                  });
      
          timer->start(1000);
      
          return future;
      }
      
      int main(int argc, char *argv[])
      {
          QCoreApplication app(argc, argv);
      
          QtFuture::makeReadyFuture(true)
                  .then([](bool){
                      return returnTrueAsync();
                  })
                  .unwrap()
                  .then([](bool list){
                      return QtFuture::makeReadyFuture(true)
                              .then([](bool) { // this then is important for the bug
                                  return true;
                              });
                  })
                  .unwrap() // this unwrap is important for the bug
                  .then([](bool list){
                      qDebug() << "Usually does not reach this";
                      qDebug() << "Usually there is an access violation exception before reaching this point";
                  });
      
          return app.exec();
      }
      

      ORIGINAL CONTENT OF TICKET:

      In my application I use the new QFuture::unwrap() a lot. In some particular cases the whole application hangs on a call to QFutureInterfaceBase::isChainCanceled() somewhere down the backtrace. I cannot isolate this issue but it's quite easy to reproduce in my application. 

      In my current debugger view I see that the pointer to one of the "d->parentData" pointer is pointing to corrupt data. It's probably pointing to an earlier parent that has been freed or was never pointing to correct data.

      Also sometimes the "d->parentData" points to another parentData which again points to the first parentData. Thus creating a inifinite loop. One has state (Started|Finished) the other ("Pending"). This never ends the loop in isChainCanceled().

      The bug does not happen when running inside gdb or valgrind. So it looks like some memory corruption bug. 

      There is also a probability that I do something stupid in my code. 

      I have added a screenshot of the most (in my opinion) useful information I could find in the debugger view, right during one of the infinite loops.

       

      The piece of code that misbehaves looks like this:

      void RetQueueViewController::loadQueuesImpl() {
          setState(Loading);
      
          authenticationService()->getValidApiToken()
                  .then([this](QString apiToken){
                      return zorgstandApiClient()->getZorgstandWidgetQueues(apiToken, widgetId()); // returns QFuture<QList<RetZorgstandQueueDto*>>
                  })
                  .unwrap()
      
                  // The then(...) below never gets invoked
      
                  .then([this](QList<RetZorgstandQueueDto*> queues){
                      clearQueues(); 
                      for(auto queue : queues){
                          addQueues(queue);
                      }
                  })
                  .onFailed([this](){
                      setState(Error);
                      qDebug() << "Failed to fetch queues";
                  })
                  .then([this](){
                      if(state() == Loading){
                          if(queues().length() == 0){
                              setState(Error);
                              return;
                          }
      
                          if(queues().length() == 1){
                              setQueue(queues().at(0).value<RetZorgstandQueueDto*>());
                              setState(Queue);
                              return;
                          }
      
                          setState(Select);
                          return;
                      }
                  });
      
          // Method ends here, doesn't wait for finished. The parent object lives long and has not been destroyed while this happens.
      }

       

       

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              sonakur Sona Kurazyan
              jorisvergeer Joris Vergeer
              Vladimir Minenko Vladimir Minenko
              Alex Blasche Alex Blasche
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes