john_who_is_doe
john_who_is_doe

Reputation: 389

Memory leakage (due to worker thread) at application close

Get leakage report (Tsrch_slave_thread, TSimpleEvent) after closing the app. I understand the app quits before the thread can free itself, but how to avoid? How to wait for the thread to finish?

Thank You.


Definition of the worker thread >>
- FreeOnTerminate is set to true
- OnTerminate : TMaster.slvsrch_termination();

Tsrch_slave_thread = class(TThread)
private
  FSW: TStopWatch;
protected
  procedure Execute; override;
public
  SimpleEvent: TSimpleEvent;
  master: TMaster;
  master_HWND: HWND;   
  procedure DoTerminate; override;
  constructor Create; 
  destructor Destroy; override;
end;

procedure Tsrch_slave_thread.Execute;
var
  text_orig: string;
  activesearch: integer;
begin
  FSW.Start;
  while not terminated do begin
    activesearch := master.CMD_LISTCNT; 
    //stopper refresh
    synchronize(procedure begin         
        with self.master do
          Fmasternode.text := FmasterDat.MstrName + ' (' + floattostr(Fsw.ElapsedMilliseconds / 1000) + 'sec - Searching)';
    end);
    if (SimpleEvent.WaitFor(2000) <> wrTimeOut) or (activesearch <> 1) then break;  
  end;
  FSW.Stop;  
end;

TMaster (holding the reference for the worker thread) >>

TMaster = class(TObject)
private
   ...
   Fslave_search_thread : Tsrch_slave_thread;
   ...
public
   ...
end;

Destructor of TMaster (i set the worker thread's terminate flag from here) >>

destructor TMaster.Destroy;
begin
  FSlaveList.free; 
  DeallocateHWnd( fMsgHandlerHWND );
  if assigned( self.Fslave_search_thread ) then self.Fslave_search_thread.terminate; 
  inherited;      
end;

Freeing TMaster instances in the main form's OnClose event handler >>

procedure TfrmCLmain.FormClose(Sender: TObject; var Action: TCloseAction);
  ...
  node := frmDevTree.JvTreeView1.Items.GetFirstNode;
  if node.data <> nil then
  begin
    while Assigned(Node) do begin
      if TObject(node.data) is TMaster then
        TMaster(node.data).free;
      node := node.getNextSibling;
    end;
  end;
  ...
  freeandnil(udpsend);
  action := caFree;
end;

Destructor of Tsrch_slave_thread >>

destructor Tsrch_slave_thread.destroy;
begin
  self.SimpleEvent.free;
  inherited;
end;

Tsrch_slave_thread.DoTerminate >>

procedure Tsrch_slave_thread.DoTerminate;
begin
  inherited;
  self.master.FlastSearchTime := self.FSW.ElapsedMilliseconds / 1000;
  self.simpleEvent.SetEvent; 
end;

Tsrch_slave_thread's OnTerminate event handler >>

procedure TMaster.slvsrch_termination(Sender: TObject);
begin
  if Assigned(Fslave_search_thread) then
  begin
    self.FLastSearchTime := Fslave_search_thread.FSW.ElapsedMilliseconds - SEARCH_DELAY_SEC;  
    Fslave_search_thread := nil;
  end;
  ... 
  postmessage ( self.FMasterDevFrmHND, WM_SLVSRCH_COMPLETE, integer(self), 0);  
end;

Upvotes: 1

Views: 144

Answers (1)

David Heffernan
David Heffernan

Reputation: 612874

Simply put you need to destroy the thread as well as terminate it. Add a call to Free immediately after you terminate the thread.

destructor TMaster.Destroy;
begin
  FSlaveList.free; 
  DeallocateHWnd(fMsgHandlerHWND);
  if assigned(Fslave_search_thread) then
    Fslave_search_thread.terminate; 
  Fslave_search_thread.Free;
  inherited; 
end;

As an aside, excessive use of Self will make the code harder to read.

Upvotes: 1

Related Questions