Re: CMutex::Lock is re-entrant?



See below...
On Mon, 12 Sep 2005 15:06:03 -0700, "ultranet" <ultranet@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote:

>"Joseph M. Newcomer" wrote:
>
>> The fact that the code in MFC is NOT what you claimed, but is even worse:
>>
>> BOOL CSyncObject::Lock(DWORD dwTimeout)
>> {
>> DWORD dwRet = ::WaitForSingleObject(m_hObject, dwTimeout);
>> if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED)
>> return TRUE;
>> else
>> return FALSE;
>> }
>>
>> proves that beyond any shadow of a doubt this implementation CANNOT be trusted! It
>> actually claims that WAIT_ABANDONED means "lock has been acquired"! This goes so far
>> beyond stupidity and incompetence that it can only be classified as criminally
>> irresponsible.
>>
>> (By the way, your interpretation of what happens in the simple case is incorrect; you
>> think that because a thread which owns the mutex would return immediately, that a thread
>> which doesn't own the mutex would return immediately. This is because you have confused
>> signaled/nonsignaled with mutex ownership. A mutex becomes nonsignaled, but it is also
>> owned, and that's the key difference; thread B would block, at least for its timeout
>> period, or until the mutex becomes abandoned; thread A doesn't care about the nonsignaled
>> state because it is already the owner).
>>
>> An abandoned mutex is a catastrophe. Not just a little glitch; a major catastrophe. It
>> always represents a seriously erroneous program, but in the presence of such an error, you
>> must still recover properly. This is nontrivial (having written code that can recover
>> from abandoned mutice, I can state this with some confidence. We had to deal with shared
>> data in the presence of clients of unknown reliability). Consequently, the simplest
>> approach is to handle abandoned mutex as invalidating all computations currently in
>> progress, and nothing that is protected by the mutex can be assumed to be intact any
>> longer, and will literally have to be abandoned. And this, too, is nontrivial.
>> joe
>So basically, you are saying that thread B would in fact block after thread
>A locks the mutex,
***
Yes, the synchronization you expect *between* threads works, because the recursive
acquisition semantics are a different thing that is happening
****
>but in general CSomething synchronization objects aren't
>reliable. Given our time frame, i'll take your word, and not try to verify
>this.
***
Actually, the verification is easy; the code from VS.NET proves beyond any shadow of a
doubt that they cannot be trusted. This actually returns "true", meaning "you have
acquired the lock successfully", when the error is "mutex abandoned", a fatal error.

The example below also demonstrates that they code is unreliable, because you can't tell
the difference bertween a timeout (meaning you might go back and retry the wait after,
say, checking for a thread shutdown event that was set) and anything else. Note that this
returns TRUE if there IS a timeout, and FALSE on either successful acquisition or
abandonment; or, if you made a transcription error and that should have been !=, then it
returns FALSE on timeout and TRUE on either acquisition or abandonment. Or on having an
illegal handle. In any case, only one of four possible cases is covered correctly, and
that means you cannot write reliable code above it.

Example: assume that you get mutex abandoned. This means "there is the possibility that
the data on which I was working is now in an inconsistent state relative to the
postconditions of mutex release", or rephrased simply, "my data is screwed". The next
attempt to acquire the mutex may then successfully pass the mutex, and work with invalid
data. This is generally considered to be in poor taste, and will either generate access
faults if you are lucky, or just silently propagate the error if you are not.
****
>
>Btw., in VC 6, i referenced the code correctly:
>BOOL CSyncObject::Lock(DWORD dwTimeout)
>{
> if (::WaitForSingleObject(m_hObject, dwTimeout) == WAIT_OBJECT_0)
> return TRUE;
> else
> return FALSE;
>}
>
>Apparently in VC version you are using, they screwed it up a little.
>
>Finally, i guess the synchronization object has owning thread along w/
>signaled/non-signalled state, which as you said makes non-owning threads
>block. That makes sense, but i wasn't sure that was the case, so wanted to
>get some feedback.
****
The real problem is that the C++ advantages of having the lock/release scoped by a
declaration are lost because they got this all wrong. For example, CSingleLock should
throw an exception if it can't acquire the lock, e.g.,

swtich(::WaitForSingleObject(...))
{
case WAIT_ERROR:
throw new CWaitException(...); // CWaitException: public CSyncException
case WAIT_ABANDONED:
throw new CAbandonedException(...); // CAbandonedException : public
CSyncException
case WAIT_TIMEOUT:
return FALSE;
case WAIT_OBJECT_0:
return TRUE;
default:
ASSERT(FALSE); // error, unknown return from ::WaitForSingleObject
throw CSomeWeirdSyncProblem(...);
}

Now that's what I'd expect in a well-designed class. Lacking something like this makes the
value of the class quesiotnable. By the way, I looked at the use of m_bAcquired as their
way of telling if the lock was acquired. m_bAcquired will be TRUE if the mutex was
abandoned. I do not wish to have my life depend on the code that uses these classes.
*****
>
>Thanks.
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.



Relevant Pages

  • Re: [Info-Ingres] Mutex & Set lockmode ...
    ... a mutex wait cannot be prolonged by ... a user can very easily provoke a lock wait state ... While there is no timeout applied to the perpetrator ... Mutexes are usually not even held across I/O, ...
    (comp.databases.ingres)
  • Re: Timer implementation
    ... timeout argument can be implemented as follows. ... # Releases the lock held in +mutex+ and waits; ... def wait ...
    (comp.lang.ruby)
  • Re: mutex lock timeout
    ... > pthread_yield and I would like to try to lock a mutex and timeout ... 10 seconds if in the mean time I cannot lock it. ... Some platforms have a pthread_mutex_lockvariant with a timeout, ... but it's not in the pthread standard so it won't be portable. ...
    (comp.unix.programmer)
  • Re: mutex lock timeout
    ... > pthread_yield and I would like to try to lock a mutex and timeout (give ... 10 seconds if in the mean time I cannot lock it. ... waitable lock is not a mutex. ... lock it and another thread unlock it? ...
    (comp.unix.programmer)
  • Re: Recursive mutex that can be waited upon (pthread)
    ... While you can use a mutex to avoid that data is changed, for me having a mutex does not mean that data is not changed, it only means that data is not changed by a different thread. ... My own thread may of course change the data, hence functions I call may want to change the data and if they do so, they must be sure that these changes are atomically, hence they must lock the object and they simply can't rely that I locked the object before -> thus I need recursive locks. ... Then I could as well throw out threads of my code and just use a single thread going through an event queue. ... you have a predicate condition on an invariant. ...
    (comp.programming.threads)

Loading