Windows Mutex Object

20-Feb-1999

The Mutex object is probably the simplest and most useful of all synchronization objects. Most of the other objects can be implemented by solely using mutex objects. Its basic purpose is to provide safe manipulation of a block of data across threads. In this C++ implementation, we'll consider a class such as this:

class CMutex
{

  public:
  void Lock();    // Lock the mutex before accessing the data.
  void Unlock();  // Unlock the mutex when done with the data.

};

Such an object would be used, in the silly example proposed in Windows Synchronization Objects, in this manner:

Data:

int Var1;
int Var1plus2;
CMutex Var1Mutex;

Thread 1:

Var1Mutex.Lock();
Var1      = 5;
Var1plus2 = 7;
Var1Mutex.Unlock();

Thread 2:

Var1Mutex.Lock();
Var1      = 3;
Var1plus2 = 5;
Var1Mutex.Unlock();

Now, if Thread 1 is interrupted just after the Var1 = 5 line, and Thread 2 attempts to cross its Lock() call, Thread 2 will pause and stay frozen until Thread 1 calls its Unlock() call. Therefore, the data is never updated improperly.

In a more general way, the mutex object can be viewed as allowing only, at most, one thread to be within a fragment of the code that is enclosed between calls to the Lock() and Unlock() methods of the same mutex object. This raises a few questions:

  1. What is the behaviour if Unlock() is called before Lock()?
  2. What is the behaviour if Lock() and Unlock() calls are nested?
  3. What is the behaviour if Unlock() is not called after Lock()?

The answer to questions 1 and 2 is, of course implementation-dependent. For our particular case, which is the implementation of all this in the Windows environment, we can extract some information from the Windows API and extrapolate. For the third question, the answer is that no other thread will be able to get past the Lock() call. If the thread that didn't call Unlock() attempts to enter another pair of Lock() and Unlock() calls for the same mutex object, then the behaviour depends on the answer to the second question.

First, we'll see a possible Windows implementation of the CMutex class. We'll call it CThreadMutex, for reasons that will become clear later:

class CMutex
{

  private:
  CRITICAL_SECTION cs;   // Windows' basic mutex object.

  public:
  void Lock() {
    EnterCriticalSection(&cs);
  }

  void Unlock() {
    LeaveCriticalSection(&cs);
  }

  CMutex() {
    InitializeCriticalSection(&cs);
  }

  ~CMutex() {
    DeleteCriticalSection(&cs);
  }

};

Following the Windows API, we can see that the answers to the questions raised are:

  1. The mutex is left in an undefined state, and other threads can enter an internal infinite loop if they call Lock().
  2. The Lock() call always succeeds inmediately in the internal pair(s) of Lock() and Unlock() calls.
  3. There's an aditional consideration here. If a thread doesn't call Unlock() and terminates its execution, then the status of the mutex object becomes indeterminate, and it won't function properly.

All this brings us into the very first rule of multi-threaded programming:

  1. Programming threads is a very delicate process. It must be done VERY CAREFULLY. Mistakes are often very hard to track.

All trademarked things I mention here are TM by their respective owners. If you are one of those owners and want to be specifically mentioned, please, contact me and I'll include it.

Go back to the main index of JCAB's Rumblings

Wow! Very large number here... :) hits and increasing...

To contact JCAB: jcab@JCABs-Rumblings.com

Last updated: [an error occurred while processing this directive]


Did you like this page? Did you dislike it? Do you have any comments or questions? This is your chance! Just type some text in the box below and click on the "send" button.

Your name:

Your email:

Subject: