After dismissing the first block as
"Wrong", Leo acknowledges later that
this is something that can occur if a
function that obtains a lock calls
another function which obtains the
same lock. Fine - there is a real
danger here to avoid, and the example
is not so much "wrong" as an easy trap
to fall into through careless
programming.
The "wrong" first block is always wrong and should never be something you do, whether explicitly or by accident. You cannot use a CSingleLock to obtain multiple locks at the same time.
As its name suggests, CSingleLock is an object which manages one lock on one synchronization object. (The underlying synchronization object may be capable of being locked multiple times, but not via just a single CSingleLock.)
I meant that the other two code-blocks were situations you could run into legitimately.
You never need to lock the same CCriticalSection if you already have a lock on it (since you only need one lock to know you own the object), but you may lock it multiple times (usually as a result of holding the lock, then calling a function which gets the lock itself in case it is called by something that doesn't already have it). That's fine.
But the second and third examples
confuse me completely... because we
have one sync object (the
CCriticalSection crit) which is used
for two CSingleLock locks... implying
that crit is not a lockable thing at
all, but only the mechanism which does
the locking for an independent object
or objects.
You can lock a CCriticalSection directly (and multiple times if you want to). It has Lock and Unlock methods for doing that.
If you do that, though, you have to ensure that you have matching Unlock calls for every one of your Lock calls. It can be easy to miss one (especially if you use early returns or exceptions where an Unlock later in a function may be bypassed entirely).
Using a CSingleLock to lock a CCriticalSection is usually better because it will release the lock it holds automatically when it goes out of scope (including if you return early, throw an exception or whatever).
Can anyone point to documentation that
definitively says you can create two
locks using a single sync object as
Leo has suggested here?
Although I couldn't find the source, CCriticalSection (like most MFC objects) is almost certainly a very thin wrapper around the Win32 equivalent, in this case CRITICAL_SECTION. The documentation on EnterCriticalSection tells you:
After a thread has ownership of a
critical section, it can make
additional calls to
EnterCriticalSection or
TryEnterCriticalSection without
blocking its execution. This prevents
a thread from deadlocking itself while
waiting for a critical section that it
already owns. The thread enters the
critical section each time
EnterCriticalSection and
TryEnterCriticalSection succeed. A
thread must call LeaveCriticalSection
once for each time that it entered the
critical section.