Recent

Author Topic: why KillThread cause memory leak ?  (Read 14759 times)

mohsenti

  • Jr. Member
  • **
  • Posts: 58
why KillThread cause memory leak ?
« on: January 01, 2017, 02:03:17 pm »
I write simple thread manager with support kill task but memory manager failed to handle pointers when using kill task and produce memory leak and sometimes access volition exception.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button4Click(Sender: TObject);
  2. var
  3.   I: Integer;
  4. begin
  5.   for I := 0 to 3 do
  6.   begin
  7.     tt := TTestThread.Create(True);
  8.     tt.FreeOnTerminate := False;
  9.     tt.Start;
  10.     // Sleep(100);
  11.     KillThread(tt.Handle);
  12.     // tt.WaitFor;
  13.     tt.Free;
  14.     Sleep(1000);
  15.   end;
  16. end;
  17.  

note : That codes works fine in delphi 7.0+.

Example project was attached to post attachments.
« Last Edit: January 01, 2017, 02:07:15 pm by mohsenti »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Re: why KillThread cause memory leak ?
« Reply #1 on: January 01, 2017, 03:46:48 pm »
Seems the current implementation starts to fast, uncommenting the "sleep(100)" works fine here (or use a TEvent).

I had similar problems in XE3/4+ versions.

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #2 on: January 02, 2017, 05:53:52 am »
Seems the current implementation starts to fast, uncommenting the "sleep(100)" works fine here (or use a TEvent).

Why memory manager can't release memory of TPicture if kill thread before finish ?

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Re: why KillThread cause memory leak ?
« Reply #3 on: January 02, 2017, 07:27:24 am »
Seems the current implementation starts to fast, uncommenting the "sleep(100)" works fine here (or use a TEvent).

Why memory manager can't release memory of TPicture if kill thread before finish ?

Because it has not enough knowledge to do so.

The best solution is a TEvent that is waited on after .start and signaled in .execute.   

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: why KillThread cause memory leak ?
« Reply #4 on: January 02, 2017, 11:37:09 am »
Isn't the default solution to this to do tt.Terminate and then have the Execute do a loop that keeps checking on Terminated? If you don't use FreeOnTerminate you would have to waitfor and free.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Re: why KillThread cause memory leak ?
« Reply #5 on: January 02, 2017, 11:43:06 am »
Isn't the default solution to this to do tt.Terminate and then have the Execute do a loop that keeps checking on Terminated? If you don't use FreeOnTerminate you would have to waitfor and free.

But your mainprogram must also wait. This is what the tevent handles.

Well, it is strange to killthread so soon after creation. Killthread is mainly for non responsive threads, which I assume the OP has.

I had some problems, most in the Delphi IDE (slower startup due to debugger) that if I pressed F9, but then realized that I forgot something and pressed alt-F4 to shutdown cleanly, it would crash. The construct with tevent made sure that the thread was fully started before attempting to shut it down.

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #6 on: January 03, 2017, 09:12:46 am »
Quote
Because it has not enough knowledge to do so.

Why delphi memory manager doesn't any problem with it ?

TEvent is useful to handle starting of thread but does not affect on memory manager knowledge.
« Last Edit: January 03, 2017, 09:21:22 am by mohsenti »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Re: why KillThread cause memory leak ?
« Reply #7 on: January 03, 2017, 09:35:13 am »
Quote
Because it has not enough knowledge to do so.

Why delphi memory manager doesn't any problem with it ?

As said, some delphi's have the same problem. It probably depends on accidental timing details. (which is why uncommenting the sleep(100) helps)

Quote
TEvent is useful to handle starting of thread but does not affect on memory manager knowledge.

The problem is that you terminate a thread only halfway through its startup.

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #8 on: January 04, 2017, 06:14:20 am »
Quote
The problem is that you terminate a thread only halfway through its startup.
That is not problem.
I added TEvent to procedure but it didn't solve the problem.

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TForm1.Button4Click(Sender: TObject);
  3. var
  4.   I: integer;
  5. begin
  6.   event := TEvent.Create(nil, True, True, 'SyncEvent');
  7.  
  8.   event.ResetEvent;
  9.   for I := 0 to 3 do
  10.   begin
  11.     tt := TTestThread.Create(True);
  12.     tt.FreeOnTerminate := False;
  13.     tt.Start;
  14.     event.WaitFor(INFINITE);
  15.     event.ResetEvent;
  16.     KillThread(tt.Handle);
  17.     // tt.WaitFor;
  18.     //tt.Free;
  19.     Sleep(1000);
  20.   end;
  21.   event.Free;
  22. end;    
  23.  
  24.  

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TTestThread.Execute;
  3. var
  4.   x: ValReal;
  5. begin
  6.   //while True do
  7.   form1.event.SetEvent;
  8.   begin
  9.     p.LoadFromFile('3.jpg');
  10.     //p.Clear;
  11.   end;
  12. end;    
  13.  
  14.  

Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: why KillThread cause memory leak ?
« Reply #9 on: January 04, 2017, 06:35:30 am »
Why do you still insist on KillThread?
Plz. THAT IS WRONG!!
Call TThread.Terminate (this sets terminated only)  and wait for the event.

KillThread is a last resort, NOT a means of killing a normal thread, so memory leaks should be expected....
Read what Marco suggested again, and don't use KillThread.
See also this http://stackoverflow.com/questions/4044855/how-to-kill-a-thread-in-delphi
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #10 on: January 04, 2017, 07:09:14 am »
Quote
Why do you still insist on KillThread?

I know about TThread.terminate.
I want to interrupted the thread because TThread.terminate waste much time of cpu and I don't have any control in internal operation, so I test it in Delphi7 and kill the thread in the middle and everything goes right as I showed you in the code but in fpc memory manager will not clean the memory.

For example in picture viewer application , if I load big pictures without killing thread and releasing memory of it , application goes to OutOfMemory exception.

balazsszekely

  • Guest
Re: why KillThread cause memory leak ?
« Reply #11 on: January 04, 2017, 08:22:55 am »
Quote
@moshenti
I want to interrupted the thread because TThread.terminate waste much time of cpu and I don't have any control in internal operation
I suppose the p.LoadFromFile('3.jpg') takes to much time/resource, so load the jpg in chunks, allowing the thread to exit whenever it's needed, like this:
 
Code: Pascal  [Select][+][-]
  1. procedure TTestThread.Execute;
  2. const
  3.   BufferSize = 1048576; //set whatever value you find appropriate
  4. var
  5.   Fs: TFileStream;
  6.   Ms: TMemoryStream;
  7.   TotalCount: Int64;
  8.   BytesToRead: Integer;
  9.   Buffer: PByte;
  10. begin
  11.   Fs := TFileStream.Create('3.jpg', fmOpenRead or fmshareDenyWrite);
  12.   try
  13.     TotalCount := 0;
  14.     Ms := TMemoryStream.Create;
  15.     try
  16.       Ms.SetSize(Fs.Size);
  17.       GetMem(Buffer, BufferSize);
  18.       repeat
  19.         if FNeedToBreak then    //<- this is the most important part
  20.           Break;
  21.         if TotalCount + BufferSize > Fs.Size then
  22.           BytesToRead := FS.Size - TotalCount
  23.         else
  24.           BytesToRead := BufferSize;
  25.         Fs.Read(Buffer^, BytesToRead);
  26.         Ms.Write(Buffer^, BytesToRead);
  27.         TotalCount := TotalCount + BytesToRead;          
  28.       until TotalCount >= FS.Size;
  29.       FreeMem(Buffer);
  30.       Ms.Position := 0;
  31.       //do other small operations here, every loop must contain the: "if FNeedToBreak then Break;" part          
  32.     finally
  33.       Ms.free;
  34.     end;
  35.   finally
  36.     Fs.Free;
  37.   end;
  38. end;

Start your thread:
Code: Pascal  [Select][+][-]
  1.   tt := TTestThread.Create(True);
  2.   tt.FreeOnTerminate := True;
  3.   tt.Start;

When you want to terminate the thread immediately, just do this:
Code: Pascal  [Select][+][-]
  1.   tt.NeedToBreak := True;
  2.   tt.Terminate;

PS: And make sure you use the synchronize method when updating the GUI, otherwise bad things start to happen quickly. Even better, you should use queues to update the main thread since is asynchronous, but be careful when closing the main form not to cause deadlock.
« Last Edit: January 04, 2017, 08:30:56 am by GetMem »

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #12 on: January 04, 2017, 08:54:28 am »
I'm not a newbie and I know the best way to finish a thread is to terminate it but in this case I don't have control on the internal code and I just can kill the thread to finish it in the case on cancellation but the problem is in fpc memory manager will not clean the memory but Delphi7 does and everything goes perfectly right. So please help in the question needs not a perfect way to kill a thread.
The thing is I run a custom procedure in the thread and it does not have a exit way, and also KillThread is there for a reason and I want to use it for this force reason but memory manager doesn't let me.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Re: why KillThread cause memory leak ?
« Reply #13 on: January 04, 2017, 09:05:37 am »
Quote
The problem is that you terminate a thread only halfway through its startup.
That is not problem.
I added TEvent to procedure but it didn't solve the problem.

Well, then I can't reproduce your problem. Can you reproduce my observation that the sleep(100) after .start in the original code kills the memory leak?
 

mohsenti

  • Jr. Member
  • **
  • Posts: 58
Re: why KillThread cause memory leak ?
« Reply #14 on: January 04, 2017, 09:34:07 am »
Quote
Well, then I can't reproduce your problem. Can you reproduce my observation that the sleep(100) after .start in the original code kills the memory leak?
yes.

I tested again and Delphi code will work but fpc code will not.

FPC code.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   ExtCtrls, Math, syncobjs;
  10.  
  11. type
  12.  
  13.   { TTestThread }
  14.  
  15.   TTestThread = class(TThread)
  16.   protected
  17.     p: TPicture;
  18.     procedure Execute; override;
  19.   public
  20.     constructor Create(CreateSuspended: boolean; const StackSize: SizeUInt = DefaultStackSize);
  21.     destructor Destroy; override;
  22.   end;
  23.  
  24.   { TForm1 }
  25.  
  26.   TForm1 = class(TForm)
  27.     Button4: TButton;
  28.     Button5: TButton;
  29.     procedure Button4Click(Sender: TObject);
  30.     procedure Button5Click(Sender: TObject);
  31.   private
  32.  
  33.   public
  34.     tt: TTestThread;
  35.     event: TEvent;
  36.   end;
  37.  
  38. var
  39.   Form1: TForm1;
  40.  
  41. implementation
  42.  
  43. {$R *.lfm}
  44.  
  45. { TForm1 }
  46. procedure TForm1.Button4Click(Sender: TObject);
  47. var
  48.   I: integer;
  49. begin
  50.   event := TEvent.Create(nil, True, True, 'SyncEvent');
  51.   event.ResetEvent;
  52.   tt := TTestThread.Create(True);
  53.   tt.FreeOnTerminate := False;
  54.   tt.Start;
  55.   event.WaitFor(INFINITE);
  56.   event.ResetEvent;
  57.   event.Free;
  58. end;
  59.  
  60. procedure TForm1.Button5Click(Sender: TObject);
  61. begin
  62.   KillThread(tt.Handle);
  63.   tt.Free;
  64. end;
  65.  
  66. { TTestThread }
  67.  
  68. procedure TTestThread.Execute;
  69. var
  70.   x: ValReal;
  71. begin
  72.   form1.event.SetEvent;
  73.   p.LoadFromFile('1.bmp');
  74.   while True do
  75.   begin
  76.     //Do stuff and we will stop it in the middle
  77.   end;
  78. end;
  79.  
  80. constructor TTestThread.Create(CreateSuspended: boolean; const StackSize: SizeUInt);
  81. begin
  82.   inherited Create(CreateSuspended, StackSize);
  83.   p := TPicture.Create;
  84. end;
  85.  
  86. destructor TTestThread.Destroy;
  87. begin
  88.   p.Free;
  89.   WriteLn('Free');
  90.   inherited Destroy;
  91. end;
  92.  
  93. end.
  94.  

delphi code.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs,StdCtrls,SyncObjs,
  8.   ExtCtrls, Math;
  9.  
  10. type
  11.    TTestThread = class(TThread)
  12.   protected
  13.     p: TPicture;
  14.     procedure Execute; override;
  15.   public
  16.     constructor Create(CreateSuspended: Boolean);
  17.     destructor Destroy; override;
  18.   end;
  19.  
  20.   TForm1 = class(TForm)
  21.     Button1: TButton;
  22.     Button2: TButton;
  23.     procedure Button1Click(Sender: TObject);
  24.     procedure Button2Click(Sender: TObject);
  25.   private
  26.     { Private declarations }
  27.   public
  28.     tt: TTestThread;
  29.         event: TEvent;
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35. implementation
  36.  
  37. {$R *.dfm}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. var
  41.   I: Integer;
  42.   dd:DWORD;
  43. begin
  44.   event := TEvent.Create(nil, True, True, 'SyncEvent');
  45.   event.ResetEvent;
  46.   tt := TTestThread.Create(True);
  47.   tt.FreeOnTerminate := False;
  48.   tt.Resume;
  49.   event.WaitFor(INFINITE);
  50.   event.ResetEvent;
  51.   event.Free;
  52. end;
  53.  
  54. { TTestThread }
  55.  
  56. constructor TTestThread.Create(CreateSuspended: boolean);
  57. begin
  58.    inherited Create(CreateSuspended);
  59.   p := TPicture.Create;
  60. end;
  61.  
  62. destructor TTestThread.Destroy;
  63. begin
  64.     p.Free;
  65.   inherited;
  66. end;
  67.  
  68. procedure TTestThread.Execute;
  69. begin
  70.   form1.event.SetEvent;
  71.   p.LoadFromFile('1.bmp');
  72.   while True do
  73.   begin
  74.     //Do stuff and we will stop it in the middle
  75.   end;
  76. end;
  77.  
  78. procedure TForm1.Button2Click(Sender: TObject);
  79. var
  80.   dd:DWORD;
  81. begin
  82.      TerminateThread(tt.Handle,dd);
  83.        tt.Free;
  84. end;
  85.  
  86. end.
  87.  

 

TinyPortal © 2005-2018