Re: Delphi 2010 TTHread

Giganews Newsgroups
Subject:Re: Delphi 2010 TTHread
Posted by: Remy Lebeau (TeamB) (no.spam@no.spam.com)
Date:Mon, 9 Nov 2009

<Luis Cobian> wrote in message news:1813…@forums.codegear.com...

> I have a global Mutex: Thandle; a main app and 2 threads.
>
> The main app creates the mutex

Unless the mutex needs to be being shared with other external applications, then you should not be assigning a name to it at all:

{code:delphi}
Mutex := CreateMutex(nil, False, nil);
{code}

> before closing the app it checks that nobody owns the mutex.
> If somebody else owns it, wait.

If you terminate and free your worker threads properly, then there would be no need to wait on the mutex itself, as it will always be unowned by the time your code gets that far.

> if WaitForSingleObject(Mutex,Infinite) then

That is not the correct way to check WaitForSingleObject()'s return value.  WaitForSingleObject() (and other waiting functions) does not return a Boolean value, it returns an Integer that can contain one of several different values that mean different things.  You should be checking for those values explitically.

> The second thread spawns a third thread with FreeonTeminate:= true.
> Now to the problem. This third thread adquires the mutex on the
> constructor and releases it on the destructor.

When FreeOnTerminate is True, the destructor is not called in the same thread context that the constructor was called in.  As such, the destructor will not be able to release the mutex at all.  When the thread terminates while still owning the mutex, the mutex will become abandoned, and any pending wait will return WAIT_ABANDONED instead of WAIT_OBJECT_0.

You should make the thread acquire the mutex in its Execute() method instead of the constructor, and release it at the end of Execute(), or in an overriden DoTerminate(), instead of the destructor.  That way, the mutex is acquired and released in the same thread context correctly.

> This is always OK.

You have a race condition.  If you wait on the mutex immediately after starting the second thread, there is no guarantee that the third thread has actually begun running yet before the first thread enters the wait.

You also have a logic bug.  If the third thread terminates (or worse, dies unexpectedly) before the first thread enters the wait, the third thread will not have released the mutex correctly (because of the context issue I mentioned above), and the wait will fail, but your code is not checking for that condition correctly.

> Now, when I destroy the thread:

That code won't work, for the reasons mentioned above.  In fact, you are best off sincely not waiting on the mutex at all.  A thread's handle becomes signaled when the thread terminates, and can be used with the WaitFor...() functions.  So while the second thread is terminating, it can wait for the third thread's handle to become signaled before finishing its own cleanup.  When the first thread is shutting down the second thread, it can wait on the second thread's handle to become signaled.  This is a muc
h more reliable approach then using a single mutex to synchronize all thread threads with each other.

> The problem is that even if the thread IS the owner of the mutex (done and
> checked on the constructor), the ReleaseMutex sometimes reports that the
> Mutex cannot be released because the thread is NOT the owner.

Because it really is not, because of the issues I mention above.

> Funny thing is, if both WaitForSingleObject AND releaseMutex are located
> in the same procedure (like Execute) everything is always OK.

Of course, since they are being called within a single thread context.

--
Remy Lebeau (TeamB)

Replies

In response to

Delphi 2010 TTHread posted by Luis Cobian on Mon, 9 Nov 2009