Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-01

Possible Memory Leak in TThread.Synchronize

4 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Tony Whyman

I have been investigating a possible memory leak in a multi-threading Lazarus program compiled with 3.0.4 where the stack trace with heaptrc shows:

Heap dump by heaptrc unit
7014 memory blocks allocated : 131978472/131992352 7004 memory blocks freed     : 114543216/114557096 10 unfreed memory blocks : 17435256
True heap size : 19267584
True free heap : 1831200
Should be : 1831048
Call trace for block $00007FFFE2A9D0C0 size 96
  $000000000043B31A line 354 of ../inc/heap.inc
  $0000000000450FE8 line 888 of ../unix/cthreads.pp   $000000000043DB9F line 294 of ../inc/thread.inc   $00000000004AE6B4 line 322 of ../objpas/classes/classes.inc   $00000000004AE754 line 344 of ../objpas/classes/classes.inc   $00000000004AE7F0 line 357 of ../objpas/classes/classes.inc   ....

Line 322 of classes.inc reads:

     FSynchronizeEntry^.SyncEvent := RtlEventCreate;

Hence the implication is that the SyncEvent is not being disposed of correctly. Later on TThread.DoneSynchronizeEvent reads

procedure TThread.DoneSynchronizeEvent;
   begin
     if not Assigned(FSynchronizeEntry) then
       Exit;
     RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
     Dispose(FSynchronizeEntry);
     FSynchronizeEntry := Nil;
   end;

So no problem here it seems. However, investigating further, this is not the only place that a SynchronizeEvent is disposed of. At line 379 of classes.inc we see:

function CheckSynchronize(...)

and this ends with:

     else
       begin
       { for Queue entries we dispose the entry and raise the exception }
       Dispose(tmpentry);
       if Assigned(exceptobj) then
         raise exceptobj;
       end;
     tmpentry := PopThreadQueueHead;
     end;

The line "Dispose(tmpentry);" also disposes of a SynchronizeEvent but, unlike TThread.DoneSynchronizeEvent, there is no RtlEventDestroy.

Am I correct in pointing the finger here for the memory leak?

Tony Whyman

MWA

Reply by email

Michael Van Canneyt
to Tony Whyman
Tony Whyman wrote 5 lines
The line "Dispose(tmpentry);" also disposes of a SynchronizeEvent but, unlike TThread.DoneSynchronizeEvent, there is no RtlEventDestroy. Am I correct in pointing the finger here for the memory leak?

I doubt it, since AFAIK the RTL event is a OS object, and as such is not allocated on the heap ?

Michael.

Reply by email

Tony Whyman
to Michael Van Canneyt

Thanks, I found the problem - the thread was not being destroyed correctly on completion and this manifested itself in what looked like a weird memory leak.

Michael Van Canneyt wrote 19 lines
On Wed, 3 Jan 2018, Tony Whyman wrote: > > The line "Dispose(tmpentry);" also disposes of a SynchronizeEvent > but, unlike TThread.DoneSynchronizeEvent, there is no RtlEventDestroy. > > Am I correct in pointing the finger here for the memory leak? I doubt it, since AFAIK the RTL event is a OS object, and as such is not allocated on the heap ? Michael. _______________________________________________ fpc-pascal maillist  -  http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Sven Barth
to Tony Whyman: I have been investigating a possible memory leak in a multi-threading Lazarus program…

Am 03.01.2018 12:32 schrieb "Tony Whyman" :

function CheckSynchronize(...)

and this ends with:

    else
      begin
      { for Queue entries we dispose the entry and raise the exception }
      Dispose(tmpentry);
      if Assigned(exceptobj) then
        raise exceptobj;
      end;
    tmpentry := PopThreadQueueHead;
    end;

The line "Dispose(tmpentry);" also disposes of a SynchronizeEvent but, unlike TThread.DoneSynchronizeEvent, there is no RtlEventDestroy.

Am I correct in pointing the finger here for the memory leak?

I know you already found your problem, but nevertheless as explanation: this code is only reached by asynchronously added events (TThread.Queue()) and there no event is needed, this none is freed or even allocated.

Regards,
Sven

Reply by email