I’m looking at a simple class I have to manage critical sections and locks, and I’d like to cover this with test cases. Does this make sense, and how would one go about doing it? It’s difficult because the only way to verify the class works is to setup very complicated threading scenarios, and even then there’s not a good way to test for a leak of a Critical Section in Win32. Is there a more direct way to make sure it’s working correctly?
Here’s the code:
CriticalSection.hpp:
#pragma once
#include <windows.h>
#include <boost/shared_ptr.hpp>
namespace WindowsAPI { namespace Threading {
class CriticalSectionImpl;
class CriticalLock;
class CriticalAttemptedLock;
class CriticalSection
{
friend class CriticalLock;
friend class CriticalAttemptedLock;
boost::shared_ptr<CriticalSectionImpl> impl;
void Enter();
bool TryEnter();
void Leave();
public:
CriticalSection();
};
class CriticalLock
{
CriticalSection &ref;
public:
CriticalLock(CriticalSection& sectionToLock) : ref(sectionToLock) { ref.Enter(); };
~CriticalLock() { ref.Leave(); };
};
class CriticalAttemptedLock
{
CriticalSection &ref;
bool valid;
public:
CriticalAttemptedLock(CriticalSection& sectionToLock) : ref(sectionToLock), valid(ref.TryEnter()) {};
bool LockHeld() { return valid; };
~CriticalAttemptedLock() { if (valid) ref.Leave(); };
};
}}
CriticalSection.cpp:
#include "CriticalSection.hpp"
namespace WindowsAPI { namespace Threading {
class CriticalSectionImpl
{
friend class CriticalSection;
CRITICAL_SECTION sectionStructure;
CriticalSectionImpl() { InitializeCriticalSection(§ionStructure); };
void Enter() { EnterCriticalSection(§ionStructure); };
bool TryEnter() { if (TryEnterCriticalSection(§ionStructure)) return true; else return false; };
void Leave() { LeaveCriticalSection(§ionStructure); };
public:
~CriticalSectionImpl() { DeleteCriticalSection(§ionStructure); };
};
void CriticalSection::Enter() { impl->Enter(); };
bool CriticalSection::TryEnter() { return impl->TryEnter(); };
void CriticalSection::Leave() { impl->Leave(); };
CriticalSection::CriticalSection() : impl(new CriticalSectionImpl) {} ;
}}
Here are three options and personally I favour the last one…
However….
I’m dubious of your design choice. Firstly there’s too much going on in the class (IMHO). The reference counting and the locking are orthogonal. I’d split them apart so that I had a simple class that did critical section management and then built on it I found I really needed reference counting… Secondly there’s the reference counting and the design of your lock functions; rather than returning an object that releases the lock in its dtor why not simply have an object that you create on the stack to create a scoped lock. This would remove much of the complexity. In fact you could end up with a critical section class that’s as simple as this:
Which fits with my idea of this kind of code being simple enough to eyeball rather than introducing complex testing …
You could then have a scoped locking class such as:
You’d use it like this
Of course my code isn’t using
TryEnter()or doing anything complex but there’s nothing to stop your simple RAII classes from doing more; though, IMHO, I thinkTryEnter()is actually required VERY rarely.