Critical Section
Saturday 17 August 2002
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;
};