john_who_is_doe
john_who_is_doe

Reputation: 389

Deadlock at freeing thread

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

Answers (1)

David Heffernan
David Heffernan

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:

  1. Thread Execute completes and the thread runs its termination code.
  2. Thread termination code is synchronized onto the main thread. At this point the slave thread is waiting for the main thread via the call to Synchronize.
  3. Thread termination code, running on the main thread, destroys the slave thread. This leads to a 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

Related Questions