CS locking is managed by a lock count. If you enter it twice and exit one, you have still one lock owned, and hence no
other thread can lock the CCS (they all will wait for your thread to release all locks, that is: all your "entrance" must be "leaved" before someone else can "enter").
But note that, in loop, like
for(.,.,.)
{
EnterCriticalSection(.);
EnterCriticalSection(.);
LeaveCriticalSection(.);
}
there is just one thread: so it will run the loop nicely. It will add 2 remove one for each iteration.
Wen it exists, you're left with a number of lock pending.
In anoter case like
EnterCriticalSection(.);
for(.,.,.)
{
EnterCriticalSection(.);
LeaveCriticalSection(.);
}
LeaveCriticalSection(.)
everithing is fine. Of course, the inner Enter/Leave in this case have no practical effect, but they can stay inside a function body that can be called either inside or outside the loop.
In general a good practice is to place an equal number of Enter/Leave into a same scope (
{}
pairs) or let them be managed by a on-stack variable contruction / destruction, like
class ccs_locker
{
CRITICAL_SECTION* pcs;
public:
ccs_locker(CRRITICAL_SECTION& cs) :pcs(&cs) { EnterCriticalSection(pcs); }
~ccs_unlocker() { LeaveCriticaSection(pcs); }
private:
ccs_locker(const ccs_locker&);
ccs_locker& operator=(const ccs_locker&);
};
class criticalsection: public CRITICAL_SECTION
{
public:
criticalsection() { InitializeCriticalSection(this); }
~criticalsection() { DeleteCriticalSection(this); }
private:
criticalsection(const criticalsection&);
criticalsection& operator=(const criticalsection&);
};
{
criticalsection cs;
ccs_locker lock1(cs);
for(...)
{
ccs_locker lock2(cs);
...
if(...) return;
...
if(...) throw ...;
...
if(...) break;
...
}
return;
}
Moral: no matter how you return, throw or break: destructor will always adjust the call pairs.