Reputation: 389
Having some problem freeing up a worker thread, i just dont see why is the deadlock at freeing the thread in the thread's OnTerminate event handler (TMaster.slvsrch_termination)
.
Im using postmessage instead of synchronize in thread execute to sync some VCL control, just to avoid deadlock.
procedure Tsrch_slave_thread.Execute;
var
activesearch: integer;
begin
activesearch := 1;
FMaster.CMD_SEARCH;
FSW.Start;
while not terminated do begin
postmessage( FDevTree_HWND, WM_STOPPER_REFRESH, trunc(Fsw.ElapsedMilliseconds / 1000), integer(FMasterNode) );
//
if (SimpleEvent.WaitFor( SEARCH_DELAY_SEC ) <> wrTimeOut) or (activesearch <> 1) then break;
activesearch := Fmaster.CMD_LISTCNT;
FSW.Stop;
end;
end;
procedure Tsrch_slave_thread.DoTerminate;
begin
inherited;
self.simpleEvent.SetEvent;
end;
FreeOnTerminate property of the thread been set to false:
...
Fslave_search_thread: Tsrch_slave_thread;
...
Fslave_search_thread.FreeOnTerminate := false
Fslave_search_thread.OnTerminate := slvsrch_termination;
...
procedure TMaster.slvsrch_termination(Sender: TObject);
begin
...
if Assigned(Fslave_search_thread) then
begin
Fslave_search_thread.free; //Deadlock, why?
Fslave_search_thread := nil;
end;
...
end;
Upvotes: 2
Views: 255
Reputation: 612844
The deadlock occurs because the thread's destructor waits on the thread. It calls WaitFor
to do so. The thread's destructor is called from slvsrch_termination
. That is the OnTerminate
event handler for the thread. And OnTerminate
event handlers are executed in the main thread because they are invoked with a call to Synchronize
.
And here's the dead lock. It progresses like this:
Execute
completes and the thread runs its termination code.Synchronize
.WaitFor
on the thread. Now the main thread is waiting on the slave thread.Steps 2 and 3 are your deadlock.
Beyond the deadlock, it is also an error that you free the slave thread instance from its own OnTerminate
handler. Look at the code in the thread procedure:
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
Your OnTerminate
handler is invoked from the call to DoTerminate
. When it returns, Thread
has been destroyed, but the code still access it. What's more look at this code of yours:
procedure Tsrch_slave_thread.DoTerminate;
begin
inherited;
self.simpleEvent.SetEvent;
end;
The inherited
invokes the OnTerminate
handler. That frees the instance. Then you access the instance.
The bottom line is that you must not free the thread from its OnTerminate
handler.
Upvotes: 4