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

QThreadPool should make sure maxThreadCount is > 0 as < 1 breaks it even though the docs say otherwise.

    XMLWordPrintable

Details

    • All
    • 885eff053797d56f2e295558d0a71b030fbb1a69 (qt/qtbase/dev) cd3ce03705e32f2d1bd566fc6ad9ae215cff56b7 (qt/qtbase/6.1) c0d310d9969111713ec705db1c6c4833c92df468 (qt/qtbase/6.0)

    Description

      I have a QThreadPool that I set maxThreadCount to QThread::idealThreadCount() - 1. I didn't bother clamping the value to > 0 since the QThreadPool docs says "Note: The thread pool will always use at least 1 thread, even if maxThreadCount limit is zero or negative."

      This might have been true sometime in the past but as the code is written currently it's no longer the case.

      What happened was that my code was run on a pc with a single core which resulted in the max thread count being set to 0. I started one runner and then 30 seconds passed, which is the default expiry time for the threads, and then I tried starting some more runners which then never got started. I had my suspicion that the 0 max count was the problem but as it was threading I wanted to be sure, so after a lot of debugging I figured out that the issue is with the code in QThreadPoolPrivate::tryStart:

      bool QThreadPoolPrivate::tryStart(QRunnable *task)
      {
          Q_ASSERT(task != nullptr);
          if (allThreads.isEmpty()) {
              // always create at least one thread
              startThread(task);
              return true;
          }
      
          // can't do anything if we're over the limit
          if (activeThreadCount() >= maxThreadCount)
              return false;
      
          if (waitingThreads.count() > 0) {
              // recycle an available thread
              enqueueTask(task);
              waitingThreads.takeFirst()->runnableReady.wakeOne();
              return true;
          }
      
          if (!expiredThreads.isEmpty()) {
              // restart an expired thread
              QThreadPoolThread *thread = expiredThreads.dequeue();
              Q_ASSERT(thread->runnable == nullptr);
              ++activeThreads;
              thread->runnable = task;
              thread->start(threadPriority);
              return true;
          }
      
          // start a new thread
          startThread(task);
          return true;
      }
      

      The first if would evaluate to false since I had one expired thread but the second if would always evaluate to true since 0 >= 0, meaning the expired thread would never be recovered, which happens further down in this function.

      The solution could be to just std::max(1, maxThreadCount) as I now did in my code, or fix the logic above. Or just change the docs

       

      Attachments

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

        Activity

          People

            thiago Thiago Macieira
            cyriuz Viktor Arvidsson
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes