QtCore, QMutex class, lock function - stack overflow failure



  • Qt version = 5.1.1
    Platform = Windows 8 x64

    I’m investigating very rare error in one program. The program start with creating one master thread, which creates worker threads. Each worker thread is waiting for wait signal - QWaitCondition::wait(). After creating all worker threads master thread wakes them using QWaitCondition::wakeAll(). Then GUI appears.
    This works perfectly almost every time but sometimes (very rarely) program crashes with "program has stopped working" message and c00000fd exception code (stack overflow). I'm bad in understanding Qt internals so please help me to figure out what exactly is going on.

    Interesting part in call stack starts in worker thread when thread calls QWaitCondition::wait():

    @
    // \qtbase\src\corelib\thread\qwaitcondition_win.cpp

    bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
    {
    if (!mutex)
    return false;
    if (mutex->isRecursive()) {
    qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes");
    return false;
    }

    QWaitConditionEvent *wce = d->pre();
    mutex->unlock();
    
    bool returnValue = d->wait(wce, time);
    
    mutex->lock(); // NEXT POINT OF INTEREST
    d->post(wce, returnValue);
    
    return returnValue;
    

    }
    @

    Mutex was unlocked, then it waited for d->wait(). And finally it was about to be locked, and here is the lock() function:

    @
    // \qtbase\src\corelib\thread\qmutex.cpp

    void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
    {
    if (fastTryLock())
    return;
    QMutexData *current = d_ptr.loadAcquire();
    if (QT_PREPEND_NAMESPACE(isRecursive)(current))
    static_cast<QRecursiveMutexPrivate *>(current)->lock(-1);
    else
    lockInternal(); // NEXT POINT OF INTEREST
    }
    @

    I can see the program failed with fastTryLock() then tried to use lockInternal():

    @
    // \qtbase\src\corelib\thread\qmutex.cpp

    void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
    {
    lockInternal(-1);
    }

    bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
    {
    Q_ASSERT(!isRecursive());

    while (!fastTryLock()) {
        QMutexData *copy = d_ptr.loadAcquire();
        if (!copy) // if d is 0, the mutex is unlocked
            continue;
    
        if (copy == dummyLocked()) {
            if (timeout == 0)
                return false;
            // The mutex is locked but does not have a QMutexPrivate yet.
            // we need to allocate a QMutexPrivate
            QMutexPrivate *newD = QMutexPrivate::allocate(); // NEXT POINT OF INTEREST
    

    ... the rest of the function
    }
    @

    And again it failed to use fast locking, then it tried to allocate memory for some internal private class:

    @
    // \qtbase\src\corelib\thread\qmutex.cpp

    QMutexPrivate *QMutexPrivate::allocate()
    {
    int i = freelist()->next();

    ... the rest of the function
    }
    @

    I don't know what is freelist, I think it is a list of unused QMutexPrivate objects to be used later. Then goes some Qt's internal dark magic in "qglobalstatic.h" (QGlobalStatic::operator()), then:

    @
    // qmutex.cpp

    typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList;
    Q_GLOBAL_STATIC(FreeList, freelist);
    }
    @

    Then:

    @
    // qmutex.h

    class Q_CORE_EXPORT QMutexLocker
    {
    public:
    inline explicit QMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
    {
    Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0),
    "QMutexLocker", "QMutex pointer is misaligned");
    val = quintptr(m);
    if (Q_LIKELY(m)) {
    // call QMutex::lock() instead of QBasicMutex::lock()
    static_cast<QMutex *>(m)->lock(); // THIS IS A LOOP
    val |= 1;
    }
    }
    ... the rest of the class
    }
    @

    So mutex locks another mutex, which tries to lock itself again and again until call stack overflows. All worker thread affected. PLease let me know if you encountered error like this and you know how to resolve it of if you have a guess of programming mistake that can cause this error. Thanks.


Log in to reply
 

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