views:

465

answers:

2

Hi,

I've got some memories leak related to TidHTTP when it's waiting for the response of a server after a GET and that the thread is being terminated.

Example :

aThread = class(TThread) 
private  
  FidHTTP :TidHTTP;   
  FCommand :String;
public   
  procedure Execute(); override;   
  constructor Create(aCommand :String); override;
  procedure Disconnect;
end;

procedure aThread.Execute();   
  var response :String; 
begin   
  response := FidHTTP.Get(FCommand); 
end;

procedure aThread.Disconnect;
begin
  if ((FidHTTP <> nil) and (FidHTTP.Connected)) then FidHTTP.IOHandler.CloseGracefully;
end;

constructor aThread.Create(aCommand :String); override; 
begin   
  FCommand := aCommand;    
  inherited Create; 
end;


I stop the thread with this when the application close :

aThread.Disconnect;
aThread.Terminate;
aThread.Free;

What should I do to solve the memories leaks ?

FastMM4 Log :

13 - 20 bytes: TIdThreadSafeInteger x 1
21 - 36 bytes: EAccessViolation x 1, TIdCriticalSection x 2
181 - 212 bytes: UnicodeString x 1

Thanks :)

+7  A: 

You should call

aThread.WaitFor;

before destroying the thread. This makes sure that the thread properly terminates. Destroying the thread without terminating it probably causes an access violation in the execute method, which results in the memory leaks displayed by FastMM.

EDIT Considering the fact that the problem might be the blocking call in your execute method, you might want to set TIdHttp.ReadTimeOut to a reasonable time and check for thread termination in regular intervals.

Smasher
Thanks ! It seems to work :)
Ariel32
Better to call `WaitFor` first thing in the overridden destructor (which is missing from the quoted code) than to rely on client code to do the right thing.
mghie
You're right. I'm used to do it from the outside, so that I can do Thread1.Terminate; Thread2.Terminate; Thread1.WaitFor; Thread2.WaitFor
Smasher
@Smasher: That's true, you could even call `WaitForMultipleObjects()` on the thread handles. Note however that adding `Terminate()` and `WaitFor()` to the destructor doesn't prevent you from calling them from the outside. It's more of a safety net, really.
mghie
A: 

Indy also produces two or three expected memory leaks, like the integer and the critical section. But they might or might not have been registered as expected. So I can't tell if these are the ones you see. If you would run your code 5 times, would you see more leakage than you are seeing now?

As for the WaitFor suggested by Smasher, to call before calling Free. This should not be needed, nor the cause of your problems, because if you check the destructor of TThread, you will see exactly that is already done.

Why you are getting an Access Violation in your leak report, I don't really know. However, you are calling Disconnect from outside of your thread, while the Indy component is in use in your thread. Don't do that, using the same non-thread-safe components from different threads is asking for trouble. This might cause your Access Violation leaking. Let the thread itself do ALL the calls to the Indy component.

Reducing the ReadTimeOut as suggested by Smasher is a good idea however to make sure your application doesn't block too long.

MvdH
+1 I wasn't aware of the fact that WaitFor is called in the destructor
Smasher
@MvdH: You're wrong. `WaitFor` may be exactly what is needed, because an overridden destructor may free an object **before** the inherited destructor calls `WaitFor`. The thread **has** to be terminated (and `WaitFor` called) before anything can be freed. However, there's no need to do this from the outside, calling this from the destructor works just as well. In the end it's a design problem of the VCL, there should be something like `TThread.BeforeDestruction` which calls `WaitFor`.
mghie
@mghie: I agree that a WaitFor is needed if a descendant destructor destroys it's owned resources BEFORE calling inherited; However I consider this a bug on itself, which is then circumvented by a redundant call to WaitFor(). Most of the times when you add redundant calls, there is a structural error which should be fixed instead, but that's just MHO. :-) If an inherited scope of code uses resources, then call inherited FIRST and then release your resources. This way no redundant call to WaitFor() is needed.
MvdH