A bug about QThreadPoolPrivate::runnableReady ?



  • Recently I study QThread source code, in "void QThreadPoolThread::run()" function, we can find below codes:

    void QThreadPoolThread::run()
    {
        QMutexLocker locker(&manager->mutex);
        for(;;) {
            QRunnable *r = runnable;
            runnable = 0;
    
            do {
                if (r) {
                    const bool autoDelete = r->autoDelete();
    
    
                    // run the task
                    locker.unlock();
    #ifndef QT_NO_EXCEPTIONS
                    try {
    #endif
                        r->run();
    #ifndef QT_NO_EXCEPTIONS
                    } catch (...) {
                        qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
                                 "This is not supported, exceptions thrown in worker threads must be\n"
                                 "caught before control returns to Qt Concurrent.");
                        registerTheadInactive();
                        throw;
                    }
    #endif
                    locker.relock();
    
                    if (autoDelete && !--r->ref)
                        delete r;
                }
    
                // if too many threads are active, expire this thread
                if (manager->tooManyThreadsActive())
                    break;
    
                r = !manager->queue.isEmpty() ? manager->queue.takeFirst().first : 0;
            } while (r != 0);
    
            if (manager->isExiting) {
                registerTheadInactive();
                break;
            }
    
            // if too many threads are active, expire this thread
            bool expired = manager->tooManyThreadsActive();
            if (!expired) {
                ++manager->waitingThreads;
                registerTheadInactive();
                // wait for work, exiting after the expiry timeout is reached
                expired = !manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
                ++manager->activeThreads;
        
                if (expired)
                    --manager->waitingThreads;
            }
            if (expired) {
                manager->expiredThreads.enqueue(this);
                registerTheadInactive();
                break;
            }
        }
    }
    

    This snippet means that if current tasks are finished, runnableReady will wait for some time(30 seconds by default) like below:

    manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
    

    During this period, if QThreadPoolPrivate::enqueueTask is called, a new runnable task will be added to queue like below:

    void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
    {
        if (runnable->autoDelete())
            ++runnable->ref;
        // put it on the queue
        QList<QPair<QRunnable *, int> >::iterator at =
            qUpperBound(queue.begin(), queue.end(), priority);
        queue.insert(at, qMakePair(runnable, priority));
        runnableReady.wakeOne();
    }
    

    Then, runnableReady in QThreadPoolThread::run will be waked up. However, as we see, the member variable "runnable" in class QThreadPoolThread is NULL, so QThreadPoolThread::run will return, this is not what we want !

    In my opinion, since "runnableReady" is waked up, the thread should fetch the new runnable task in queue and execute it. In this way, QThreadPool can reuse existing threads. Was I wrong? Please give some help.



  • While there are a few guys here that can look at that, I think you are better of sending your question to the dev mailing list http://lists.qt-project.org/mailman/listinfo/development



  • @julien_lion
    Sorry, I misunderstood the code, it has no bug. Close it now.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.