Cook Computing

Critical Section

August 17, 2002 Written by Charles Cook

Discussing critical sections the other day with some colleagues I was surprised to discover that several of them did not know that a WIN32 critical section is designed to avoid a switch into kernel mode when there is no contention for ownership (and so can be considerably faster than using a mutex). However I realized the implementation is not obvious and so I set myself the task of implementing a critical section without doing any research into how others have achieved this.

I quickly devised a solution but this contained the major flaw of not supporting re-entrancy. The full solution took a bit longer but I think the code below does the job. I've done some testing and as well as working correctly it appears to have the same performance as the WIN32 critical section. As ever with multi-threaded code it is difficult to determine that the code is bug-free, a good reason for avoiding multi-threaded code wherever possible.


class CritSect
{
public:
CritSect::CritSect()
{
  curtid = 0;
  cntr = 0;
  depth = 0;
  hevt = CreateEvent(NULL, 
                     FALSE, // auto-reset
                     FALSE, // initially unsignalled
                     NULL);
}

CritSect::~CritSect()
{
  CloseHandle(hevt);
}

void CritSect::Enter()
{
  DWORD tid = GetCurrentThreadId();
  if (InterlockedIncrement(&cntr) > 1)
  {
    if (InterlockedCompareExchange(&curtid, tid, tid) == tid)
      depth++;
    else
    {
      WaitForSingleObject(hevt, INFINITE);  
      InterlockedExchange(&curtid, tid);
    }
  }
  else
    InterlockedExchange(&curtid, tid);
}

void CritSect::Leave()
{
  if (InterlockedDecrement(&cntr) > 0)
  {
    if (depth)
      depth--;
    else    
      SetEvent(hevt);
  }
}

private:
  LONG cntr;
  LONG curtid;
  LONG depth;
  HANDLE hevt;
};