automatically preventing deadlocks when using mutexes 
Author Message
 automatically preventing deadlocks when using mutexes

hi,

I have an idea I've implemented that I'd like to double check (and
share)
with the community.

the Idea is to prevent against deadlocks resulting from forgotten
unlock()s
in case of exceptions thrown, return statements and so on by using a
special
class declared locally the takes care of unlocking the mutex.
reading the supplied class interface and description should clarify my
meaning.

what I'm worried about is that mutexes and critical sections tend to
get really
complicated, and deadlocks usually result when things are not
thoroughly
thought. I just want to make sure that I'm not missing something here.

I'd appreciate any comments you might have.

thank you,
Avshi Avital

Entercept Security Technologies

the following is the description and interface of the CMutex and
CMutexHandler
classes.
reading the description of the classes will explain the idea.
for your convenience, I've included the full source code at the end of
the msg.

=======================================================================
classes interface:
=======================================================================

// *********************** CMutex *********************** //

// Description:
//      Synchronization class for providing mutual exclusion.
//      the mutex is recursible (the same thread can lock more than once,
but has to unlock
//      as many times as the mutex was locked).

class CMutex
{
//----- functions -----//

public:
        // initialize critical section.
        CMutex();

        // close critical section.
        virtual ~CMutex();

        void lock();
        void unlock();
        bool isLocked();

Quote:
};

// *********************** CMutexHandler *********************** //

// Description:
//      this class offers a safe mechanizm for handling mutexes. the class
is used
//      to prevent deadlocks. its operation is similar to that of the
auto_ptr class.
//      the constructor receives a mutex object, and the destructor makes
sure that
//      the mutex is unlocked() for every lock() call on it (to support
recursive
//      locking). this way, when the CMutexHandler goes out of scope, the
destructor
//      is called, and the mutex is unlocked.
//      using this class will make sure that the mutex is unlocked no
matter how the
//      function ended (return, exception).
//      if a CMutexHandler is used, it's important that any lock() or
unlock() operation
//      will be done on the CMutexHandler object and NOT on the CMutex
object, or
//      deadlocks might result.

class CMutexHandler
{
//----- functions -----//

public:
        CMutexHandler(CMutex & mutex);

        ~CMutexHandler();

        void lock();

        void unlock();

Quote:
};

// ********************************************************* //

=======================================================================
and here's a code example:
=======================================================================

#include "Mutex.h"

class SomeClass
{
public:
        // NOTE: if the critical section is for the duration of the function,
you can simply
        // "mutexHandler.lock()" at the begining of the function and forget
about it.
        void foo() {
                CMutexHandler mutexHandler(mutex);

                // do something...

                // critical section starting:
                mutexHandler.lock();

                // if "something"
                //              return;

                // if "something else"
                //              throw some_exception();

                // critical section done:
                mutexHandler.unlock();  // if this line is omitted the
                                                                // mutex will be unlocked anyway.
        }

private:
        CMutex mutex;

Quote:
};

void main()
{
        SomeClass s;

        s.foo();

Quote:
}

=======================================================================

full source code (implementation for Windows, Solaris and Linux, but
should work for any POSIX
supported Unix platform).
define "WIN32" on Windows platform, define "UNIX" on unix.

=======================================================================
file "Mutex.h":
=======================================================================

#ifndef CMUTEX_H
#define CMUTEX_H

///////////////////////////////////////////////////////////////////////////////////////
// File name:
//      Mutex.h
//
// General Description:
//      Mutex classes.
//      Classes are multi-platform.
//
// Author:
//      Avshi Avital
///////////////////////////////////////////////////////////////////////////////////////

#if defined(WIN32)
        #include <windows.h>
#endif // WIN32
#if defined(UNIX)
        #include <unistd.h>
    #include <pthread.h>
#endif // UNIX

// *********************** CMutex *********************** //

// Description:
//      Synchronization class for providing mutual exclusion.
//      the mutex is recursible (the same thread can lock more than once,
but has to unlock
//      as many times as the mutex was locked).

class CMutex
{
//----- functions -----//

public:
        // initialize critical section.
        CMutex();

        // close critical section.
        ~CMutex();

        void lock();
        void unlock();
        bool isLocked();

private:

//----- variables -----//

        bool bLocked;

        #if defined(WIN32)
                CRITICAL_SECTION CSlock;

                // handle to the mutex:
                HANDLE hMutex;
        #endif // WIN32
        #if defined(UNIX)
                // this mutex is used to synch the access to the CMutex class:
                pthread_mutex_t                 structure_lock; // structure access lock

                // this mutex is used as the lock for the using thread:
                pthread_mutex_t                 CSlock;         // mutex

                // owner thread of the mutex
                pthread_t               owner;

                // # of times owner locked mutex
                unsigned int    count;
        #endif // UNIX

Quote:
};

// *********************** CMutexHandler *********************** //

// Description:
//      this class offers a safe mechanizm for handling mutexes. the class
is used
//      to prevent deadlocks. its operation is similar to that of the
auto_ptr class.
//      the constructor receives a mutex object, and the destructor makes
sure that
//      the mutex is unlocked() for every lock() call on it (to support
recursive
//      locking). this way, when the CMutexHandler goes out of scope, the
destructor
//      is called, and the mutex is unlocked.
//      using this class will make sure that the mutex is unlocked no
matter how the
//      function ended (return, exception).
//      if a CMutexHandler is used, it's important that any lock() or
unlock() operation
//      will be done on the CMutexHandler object and NOT on the CMutex
object, or
//      deadlocks might result.

class CMutexHandler
{
//----- functions -----//

public:
        CMutexHandler(CMutex & mutex);

        ~CMutexHandler();

        void lock();

        void unlock();

private:

//----- variables -----//

        int lockCount;
        CMutex & mutex;

Quote:
};

#endif // CMUTEX_H

=======================================================================
file "Mutex.cpp":
=======================================================================

#include <iostream>
using namespace std;

#if defined(WIN32)
        #include <windows.h>
#endif // WIN32
#if defined(UNIX)
        #include <unistd.h>
        #include <pthread.h>
#endif // UNIX

#include "Mutex.h"

// *********************** CMutex *********************** //

#if defined(WIN32)

CMutex::CMutex()
{
        InitializeCriticalSection(&CSlock);

Quote:
}

void CMutex::lock()
{
        EnterCriticalSection(&CSlock);

        bLocked = true;

Quote:
}

void CMutex::unlock()
{
        LeaveCriticalSection(&CSlock);

        bLocked = false;

Quote:
}

CMutex::~CMutex()
{
        DeleteCriticalSection(&CSlock);

Quote:
}

#endif // WIN32

// =========================================================
#if defined(UNIX)

// Because the Solaris 2.x mutex primitive is non-recursive (i.e. a
thread
// attempting to lock a mutex that it already owns will deadlock) this
contains
// additional machinery to allow recursion.

CMutex::CMutex()
{
        pthread_mutex_init(&structure_lock, NULL);
        pthread_mutex_init(&CSlock, NULL);

        // a value that no thread can have.
        owner = 0;

        bLocked = false;

Quote:
}

CMutex::~CMutex()
{
        pthread_mutex_destroy(&structure_lock);
        pthread_mutex_destroy(&CSlock);

Quote:
}

void CMutex::lock()
{
        pthread_mutex_lock(&structure_lock);

        if (isLocked()) {
                if (owner == pthread_self()) // this thread already owns the lock
                        count++;
                else {  // a different thread owns the lock
                        // release the structure lock for other threads to use:
                        pthread_mutex_unlock(&structure_lock);

                        // wait on the lock:
                        pthread_mutex_lock(&CSlock);

                        // lock the structure lock for the rest of the function:
                        pthread_mutex_lock(&structure_lock);

                        // take ownership:
                        owner = pthread_self();
                        count = 1;
                }
        } else { // not locked
                // lock and take ownership:
                pthread_mutex_lock(&CSlock);
                owner = pthread_self();
                count = 1;
                bLocked = true;
        }

        pthread_mutex_unlock(&structure_lock);

Quote:
}

void CMutex::unlock()
{
        pthread_mutex_lock(&structure_lock);

        // if someone other than the owner tries to unlock, nothing happens.
        if ( (owner == pthread_self()) &&
                 (--count == 0)                         ) {
                        pthread_mutex_unlock(&CSlock);
                        bLocked = false;
        }

        pthread_mutex_unlock(&structure_lock);

Quote:
}

#endif // UNIX

// =========================================================

// shared by all OSs.
bool CMutex::isLocked()
{
        return bLocked;

Quote:
}

// *********************** CMutexHandler *********************** //

CMutexHandler::CMutexHandler(CMutex & mutex)
                                        : lockCount(0),
                                          mutex(mutex)
{

Quote:
}

CMutexHandler::~CMutexHandler()
{
        while ( lockCount > 0 ) {
                mutex.unlock();
                lockCount--;

                #if defined(DEBUG)
                        cout << "unlock" << endl;
                #endif // DEBUG
        }

Quote:
}

void CMutexHandler::lock()
{
        mutex.lock();

        #if defined(DEBUG)
                cout << "lock" << endl;
        #endif // DEBUG

        lockCount++;

Quote:
}

void CMutexHandler::unlock()
{
        if (lockCount > 0) {
                mutex.unlock();

                #if defined(DEBUG)
                        cout << "unlock" << endl;
                #endif // DEBUG

                lockCount--;
        }

Quote:
}

=======================================================================
end source code.
=======================================================================


Sat, 06 Dec 2003 08:09:12 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. Howto prevent border using EIP

2. Preventing using files from a previous project

3. Preventing ved from using bold fonts under xwindows

4. Preventing Spammers From Using Mail() Form

5. Mutexes end critical sections?

6. How to avoid unreferenced objects (mutexes etc)

7. Semaphores+Mutexes

8. Semaphores and Mutexes

9. Problem with threads and mutexes

10. Semaphore, mutexes, what have you, in/from Tcl

11. mutexes (mutices?) or semaphores in Tcl

12. Critical sections and mutexes

 

 
Powered by phpBB® Forum Software