1

The application I'm working on at the moment is heavily multi-threaded, with a number of resources that are shared between threads. I use QMutexes in order to protect the shared resources, though in practice there should only very rarely be contention between different threads trying to lock the same mutex at the same time.

Nevertheless, I find with distressing frequency that my application grinds to a juddering halt. When I break execution and check what's going on I find that a thread is stalled because it can't obtain a lock on the required mutex.

I've checked that the mutexes are always locked and unlocked from the same thread - indeed, almost always within the same curly-bracket block - and in my latest round of troubleshooting I've replaced all my bare QMutexes with 'nMutex' objects defined as follows:

class nMutex : public QMutex
{
public:
    nMutex() : QMutex(),
               locks(0),  
    {}

    void lock(const char * callingFilename, int callingLineNo) 
    { 
        // QMutex::lock();     // Frequently hangs, so tried the following instead...
        if (!tryLock(1000))
        {
            printf("Cannot obtain mutex at line %d of %s; locks = %d",
                   callingLineNo, callingFilename, locks);
        }
        else
        {
            if (isRecursive())
                ++locks;
            else
                locks = 1;
        }
    }

    bool tryLock(int timeout = 0)  // Hides rather than overrides QMutex's tryLock, 
                                   // which is not virtual
    {
        bool result = QMutex::tryLock(timeout);
        if (result)
        {
            if (isRecursive())
                ++locks;
            else
                locks = 1; 
        }
        return result;
    }

    bool isLocked() const { return (locks > 0); }

    int lockDepth() const { return locks; }

    void unlock()
    {
        if (!locks) return;
        QMutex::unlock();
        if (isRecursive())
            --locks;
        else
            locks = 0; 
    }

private:
    int locks;
};

...and replaced all my calls to mutex.lock() with

mutex.lock(__FILE__, __LINE__).

What I find is that this quite often fails, and when it does it always reports "locks = 0" - in other words, no other thread has the mutex already, and yet the thread that's trying to get it, can't get it. Failures occur seemingly randomly, regardless of where in my code the call to mutex.lock() is made.

At present I can see two possibilites:

(1) I'm being very stupid, and am failing to see something wrong with the above approach, or

(2) There's a bug in QMutex. This seems unlikely since I've seen no reports, and I've tried this in at least three versions of Qt 5.3 and 5.4 (including the very latest 5.4.1).

Is there a way of testing which of these two possibilities is most likely to be correct? For example, in cases where a mutex cannot be obtained, is there a way of finding out specifically why that is?

Eos Pengwern
  • 1,467
  • 3
  • 20
  • 37
  • Your above approach has at least one flaw: `locks` filed in your class is not synchronised between lock() and unlock() from different threads, and its value could be wrong. Anyway, mutexes are very basic creatures and I just cannot believe that there is a bug in QMutex. I usually debug such problems just by analyzing carefully callstack for all threads: there have to be somewhere that mutex was previously locked and not unlocked yet. – Bogdan Mar 05 '15 at 10:41
  • I'm not sure I see that, since the locks variable is only updated after the underlying QMutex has been locked or unlocked. In any case, I've spotted another problem which may be the key. As QMutex::lock() is not virtual, an instruction of the form QMutexLocker locker(&myNMutex) invokes QMutex::lock() rather than nMutex::lock(), by-passing my careful reference-counting mechanism! In my code I had a mixture of QMutexLockers and manual lock()-unlock() pairs. I've refactored to use QMutexLockers with QMutexes everywhere, and for now it seems to be working all right. – Eos Pengwern Mar 06 '15 at 10:01

0 Answers0