[Qt-interest] how to debug a deadlock

Gabriel M. Beddingfield gabrbedd at gmail.com
Wed Jul 14 16:00:16 CEST 2010



On Wed, 14 Jul 2010, Matthias Pospiech wrote:

> By putting a breakpoint at every locker (20+) I found the reason
> (calling a function with locking from a locked function).
> However I still wonder if it would be possible to get to know otherwise
> in which line the deadlock was caused.

I once had a central lock where I wanted this kind of 
tracing.[1][2][3]  I created a macro like this (see [1],
lines 34ff):

    /**
     * Convenience macro for locking the Engine.
     */
    #ifndef RIGHT_HERE
    #define RIGHT_HERE __FILE__, __LINE__, __PRETTY_FUNCTION__
    #endif

And member variables to save the location where the mutex was
last locked (see [3], lines 203ff):

         struct _locker_struct {
             const char* file;
             unsigned int line;
             const char* function;
         } __locker;

And then lines 120ff:

 	///////////////////////////////////////
 	// THE BIG LOCK
 	///////////////////////////////////////
         /* Mutex locking and unlocking
          *
          * Easy usage:  Use the RIGHT_HERE macro like this...
          *     engine->lock( RIGHT_HERE );
          *
          * More complex usage:  The parameters file and function
          * need to be pointers to null-terminated strings that are
          * persistent for the entire session.  This does *not*
          * include the return value of std::string::c_str(), or
          * QString::toLocal8Bit().data().
          *
          * Notes: The order of the parameters match GCC's
          * implementation of the assert() macros.
          */
         void lock( const char* file, unsigned int line, const char* function );
         bool try_lock( const char* file, unsigned int line, const char* function );
         void unlock();

And the implementation (see [2], lines 909ff.):

     void Engine::lock( const char* file, unsigned int line, const char* function )
     {
         d->__engine_mutex.lock();
         d->__locker.file = file;
         d->__locker.line = line;
         d->__locker.function = function;
     }



     bool Engine::try_lock( const char* file, unsigned int line, const char* function )
     {
         bool locked = d->__engine_mutex.tryLock();
         if ( ! locked ) {
             // Lock not obtained
             return false;
         }
         d->__locker.file = file;
         d->__locker.line = line;
         d->__locker.function = function;
         return true;
     }



     void Engine::unlock()
     {
         // Leave "d->__locker" dirty.
         d->__engine_mutex.unlock();
     }

This doesn't help with every deadlock, but with some kinds 
of coding errors it saves a lot of time.  In general, this 
is considered too much overhead for your standard-duty 
mutex... and slows the mutex down.

Hope this helps...

-gabriel

[1] http://gitorious.org/composite/composite/blobs/master/src/Tritium/Tritium/Engine.hpp
[2] http://gitorious.org/composite/composite/blobs/master/src/Tritium/src/Engine.cpp
[3] http://gitorious.org/composite/composite/blobs/master/src/Tritium/src/EnginePrivate.hpp



More information about the Qt-interest-old mailing list