Solved

TIdHTTP in a Thread problem

Posted on 2008-10-18
16
2,027 Views
Last Modified: 2013-11-23
Hi

I'm have an app in which a have some thread. And in one of them I'm creating a TIdHTTP object to download one file. The problem is that downloading hangs my entire app - the app becomes responsive afer the download is complete. Here is the code that i use to create the object and download the file in my thread:

              try
                HTTP := TIdHTTP.Create(nil);
                HTTP.Request.Accept := 'gzip';
                HTTP.Request.AcceptEncoding := '';
                HTTP.Request.BasicAuthentication := False;
                HTTP.Request.ContentLength := -1;
                HTTP.AllowCookies := True;
                HTTP.HandleRedirects := True;
                HTTP.ConnectTimeout := 20000;
                HTTP.ReadTimeout := 60000;
                temp := HTTP.Get(temp);
              except
                error := True;
              end;

Does anyone know what is exactly happening (why is my app non-responsive during the download) and how to resolve this problem ?

Best regards
tadeusz81
0
Comment
Question by:tadeusz81
  • 8
  • 8
16 Comments
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
that depends how you are creating the thread. here is a working example:
(this will start downlaoding the open office kit and save it to c:\temp\openoffice.exe (directory must exist). when download is finished you will get a finished message). the download is about 140 MB so it will take a while. but as you can see, the applicaiton is responsive.

I am pretty sure this is not how you create the trhead. everything that the thread must execute, except minor initializations, must be done in the execute method, not in the constructor, which is executed in the main program context.

unit Unit1;
 

interface
 

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs;
 

type

  TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;
 

var

  Form1: TForm1;
 

implementation
 

{$R *.dfm}
 

uses idhttp;
 

type

  TMyThread=class(TThread)

  public

    procedure execute; override;

    procedure done;

  end;
 

procedure TForm1.FormCreate(Sender: TObject);

begin

  with tmythread.create(false) do

    freeonterminate:=true;

end;
 

{ TMyThread }
 

procedure TMyThread.done;

begin

  showmessage('finished');

end;
 

procedure TMyThread.execute;

var h:tidhttp;

    f:tfilestream;

begin

  h:=tidhttp.create(nil);

  try

    h.HandleRedirects:=true;

    f:=tfilestream.create('c:\temp\openoffice.exe', fmcreate);

    try

      h.get('http://openoffice.bouncer.osuosl.org/?&product=OpenOffice.org&os=winwjre&lang=en-US&version=3.0.0', f);

      synchronize(done);

    finally

      freeandnil(f);

    end;

  finally

    freeandnil(h);

  end;

end;
 

end.

Open in new window

0
 

Author Comment

by:tadeusz81
Comment Utility
Your code works. But, call me stupid, I still can't find what's wrong with my app.
Here goes more detailed info on how things are done in my app:

1. There is a MainForm, on which I'm creating the thread in question:

in global vars:
  WatekGlowny : TWatekGlowny = nil; //watekglowny is in WatekGlowny.pas

this is the overrided proc that I use to create this form (I don't think that it has any meaning in my case, but just to be sure I'll add it here):
procedure TTorrentProxyMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
    begin
      Style := (Style or WS_POPUP) and (not WS_DLGFRAME);
      ExStyle := ExStyle or WS_EX_TOPMOST or WS_EX_TOOLWINDOW;
      WndParent := GetDesktopwindow;
    end;
end;

this is the proc where I create the thread:
procedure TTorrentProxyMainForm.FormCreate(Sender: TObject);
begin
(...)
      WatekGlowny := TWatekGlowny.Create;
      WatekGlowny.Priority := tpHigher;
      WatekGlowny.Resume;
(...)
end;

and now to the WatekGlowny.pas:

type
  TWatekGlowny = class(TThread)
      AntiFreeze: TIdAntiFreeze;
    private
      Proxy: TIdHTTPProxyServer;
      MainTimer: TTimer;
      procedure MainTimerTimer(Sender: TObject);
    protected
      procedure Execute; override;
    public
      constructor Create; reintroduce;
      destructor Destroy; reintroduce;
  end;

constructor TWatekGlowny.Create;
begin
  inherited Create(True);
  WatekGlownyUruchomiony := True;
  AntiFreeze := TIdAntiFreeze.Create(nil);
  AntiFreeze.OnlyWhenIdle := True;
  AntiFreeze.IdleTimeOut := 250;
  AntiFreeze.ApplicationHasPriority := True;
  AntiFreeze.Active := True;
  Proxy := TIdHTTPProxyServer.Create(nil);
  Proxy.Active := False;
  Proxy.DefaultPort := 3456;
  Proxy.ListenQueue := 500;
  Proxy.MaxConnections := 500;
  Proxy.ReuseSocket := rsFalse;
  Proxy.TerminateWaitTime := 60000; //60 sekund
  Proxy.OnException := ProxyException;
  Proxy.OnBeforeCommandHandler := ProxyBeforeCommandHandler;
  Proxy.OnHTTPDocument := ProxyHTTPDocument;
  Proxy.OnConnect := ProxyConnect;
  Proxy.OnDisconnect := ProxyDisconnect;
  MainTimer := TTimer.Create(nil);
  MainTimer.Enabled := False;
  MainTimer.Interval := 60000; //1 minuta
  MainTimer.OnTimer := MainTimerTimer;
  MainTimer.Enabled := True;
end;

destructor TWatekGlowny.Destroy;
begin
  Proxy.Active := False;
  Proxy.Destroy;
  MainTimer.Enabled := False;
  MainTimer.Destroy;
  inherited Destroy;
  FreeOnTerminate := True;
  WatekGlownyUruchomiony := False;
  Terminate;
end;

procedure TWatekGlowny.Execute;
begin
  Proxy.Active := True;
end;

procedure TWatekGlowny.MainTimerTimer(Sender: TObject);
var
  HTTP : TIdHTTP;
  temp : string;

begin
(...)some records operation and assigning url to temp
          try
            begin
              try
                HTTP := TIdHTTP.Create(nil);
                HTTP.Request.Accept := 'gzip';
                HTTP.Request.AcceptEncoding := '';
                HTTP.Request.BasicAuthentication := False;
                HTTP.Request.ContentLength := -1;
                HTTP.AllowCookies := True;
                HTTP.HandleRedirects := True;
                HTTP.ConnectTimeout := 20000; // 20sek
                HTTP.ReadTimeout := 60000; // 60sek
                temp := HTTP.Get(temp);
              except
                blad := True;
              end;
            end;
          finally
            FreeAndNil(HTTP);
          end;
(...)something is done with the result of the download
end;


Can you tell me what's wrong ??
0
 
LVL 28

Accepted Solution

by:
ciuly earned 500 total points
Comment Utility
OMG. you took the code from a form and placedx it in a thread. that is COMPLETELY WRONG.

deletye the timer, delete the antifreeze. move the code from the timer event to the execute method.
I said, everything that needs to be executed by the thread MUST be in the execute procedure of the thread. if you need to wait 1 minute before downloading, put a sleep(60000); as the first line of the execute method.
if you need to do that operation every minute, then wrap the entire code that is now in the execute method in a while liek this:

procedure trhead.execute;
var i:integer;
begin
  while not terminated do
  begin
    i:=0;// the following code is needed so that your applicaiton does not freeze when closing. check terminated as often as you can but not more often than once per 10 ms.
    while (i<120) and (not terminated) do// wait 1 minute
    begin
      inc(i);
      sleep(500);
    end;
    if terminated then
      break;
    blablala;
  end;
end;

and keep in mind that this will make your
0
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
>> and keep in mind that this will make your
ignore that line. It was valid for sleep(60000) only, which I already fixed.
0
 

Author Comment

by:tadeusz81
Comment Utility
hm. weird. from what you're saying i can understand that i can't create those objects within the thread. that's really weird because the http suspends the app and proxy works like a charm. i'll try a few ideas of mine, and come back to you with those later.
0
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
it's not about creating them in teh thread. the antifreeze is used by the indy framework to let the applicatitn main thread process it's messages and thus not freeze. it makes absolutely no sense in using it. plus that in order for the indy framework to pick it pu, you need to place both the antifreeze and the http on the same component.
trust me: there is no point in using antifreeze in a thread. it will not make things go better, it will only add a little overhead 9which sure, you as human cannot sense).

second, the timer is event driven. events are executed in the main thread. which means that the timer starts and the event is executed outside your trhead in the main thread so teh trhead doesn't do anything.

and finally, the thread finishes execution right after the execute method finishes. which in your case is in the first microsecond after the thread is executed.

get it? you start the thread, the trhead sets the proxy to active and finishes, after 1 minute, the tiemr kick in, in the main thread, and executes, and because the antifreeze and http are not on the same component, the antifreeze is not even used, so the application freezes until the download is happening.

why aren't you doing what I told you to? that is the correct way of doing this. you make me waste my time and I don't like that.
0
 

Author Comment

by:tadeusz81
Comment Utility
answer is simple... because what you told me to do doesn't fit the purpose of my thread.
0
 

Author Comment

by:tadeusz81
Comment Utility
so instead of creating the timer i should create a separate thread that takes care of the things that timer did ? (am I correct this time ?)
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 28

Expert Comment

by:ciuly
Comment Utility
look, what I explained was the translation of the behaviour you want. you want a certain piece of code to be executed every minute bya  thread. that is what my code and explanations do. I really don't see why is that so hard to understand. you do not need 2 threads, you need only one, as I described it. it takes less than 5 minutes to do the changes, test it and see that it does what you want. so why not do it?
0
 

Author Comment

by:tadeusz81
Comment Utility
my thread code is about 50kB in size - it does a lot more than i wrote. i only wrote the part that causes the problem. the code for the ontimer is about 300 lines long, so there will some rather major changes to the code involved (and if it would be a separate thread than also some major synch issues on the shared data). and as you suggest if i'd put sleep in my execute the whole thread would go to sleep for that time, and that is really not the thing that i want. need to think this through.
0
 

Author Comment

by:tadeusz81
Comment Utility
one more thing. would it be better to exchange the delphi TTimer with a winAPI timer ? or maybe the results will be the same ?
0
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
in that case I suggest that you spend a day or two learning about threads and synchronization and how it relates to VCL and the vcl thread, because if you start doing somehting and it will maybe work and then later on not, it will be hell to debug the issue if you don't udnerstand the mechanism. strat from here: http://delphi.about.com/od/kbthread/Threading_in_Delphi.htm
there are plenty of examples tehre and explanations on how to do threading in various circumstances.

but what you need to understand is that you cannot move everythiong to a trhead just because you want it to. some things are VCL and cannot be run in a thread. like some database components, which I think you are using considering some of the comments in your code.

a thread is a separate thing that runs mostly independently by the other tasks. you need to ask yourself if you really need threading.

because if you need to do some db operations and then use indy and then some other db operations and etc, you might find yourself in the situation that there is nothing to thread, because everything depends on everything so everything needs to run in the same thread, and that is the VCL.

indy, via the idantifreeze solves the UI freezes. if the UI still freezes, it's because of something else. puttin it in a trhead will sometimes not solve the problem.

what you need to do is code profiling. you need to find out what parts of teh code are actually hanging your application and see how to solve that issue. in some cases it's an iteration so you can call application.processmessages. in other cases you can use asynchronous operations so your UI again will not freeze.

to different problems there are different solutions. obcipously you thought that the indy framework is to blame and thus you move that to a thread. but what you do not understand is that te entire timer event is exeecuted ONCE every 1 minute, so moving that to the thread method solves the problem 100%. not your freeze problem, but the logical execution problem. no matter what that code does, moving it to the thread execute and synchronizing needed calls, will also make it work.
but then you say that "if i'd put sleep in my execute the whole thread would go to sleep for that time, and that is really not the thing that i want."
is totally off. try to understand that the timer code sleeps for 1 minute no matter what. placing that in the execute method does exactly the same thing. the thread executes only what is in the execute method and nothing else (sure, if you call somehting from the execute method that counts). your statement and expectation is absurd, unless you figured to put some other code in the thread as well.
in a stupid comparison, the thread is the timer. the timer sleeps for 1 minute, so does the thread. there is nothing wrong there. so why is that not what you want? you definetly are thinking about threads ina  wrong manner if you say that. so my suggestionn is you learn about threading because you are understanding the concept wrong.
0
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
>> one more thing. would it be better to exchange the delphi TTimer with a winAPI timer ? or maybe the results will be the same ?

I don't see any reason for that. TTimer uses the winapi settimer function.
0
 

Author Comment

by:tadeusz81
Comment Utility
yes, i know that TTimer uses that, but i was wondering if I create one without window handle (like ttimer does), and add a message listener for WM_PROC. that way the timer would be working "outside" the app and just sending messages.
0
 

Author Comment

by:tadeusz81
Comment Utility
sorry WM_TIMER, not WM_PROC
0
 
LVL 28

Expert Comment

by:ciuly
Comment Utility
>> and add a message listener for WM_TIMER

you're overcomplicating yourself for nothing.

and I still don't understand how will that help you in your thread. the thread is not event driven like the VCL. the thread will not interrupt execution and execute the callback function as the VCL thread does. you need to understand threading before tryiong to find solutions for your problem via threads.

as far as I am concerned the problem from this quesiton is solved. I gave working code for your problem.

if you want to understand that thread+timer is like elefants flying, fine. if not, fine again. do what yuu want with thread and timer and then come back here and tell me that it's not working and all I will be able to say is that "I told you so".

lets put it this way. I am the 3rd best delphi expert here on EE as you can see for yourself in the overall top. I am telling you some stuff here. if you believe me or not and if you accept that or not is solely your problem. as far as I am and a bunch of other people are concerned, you're wasting time goose chasing. thread and timer can be done but it will not solve your problem and requires you to radically change all your code in order to write an event driven thread, just as the VCL. you are running away from the vcl by re-inventing the vcl. it's plain stupid. there are some very rare cases when that is needed, but trust me, it's not your case. and that is because you are using VCL in threaded mode which means thta some actions will need to be executed in the VCL main thread anyway.

you had a working code in the VCL environment. and I know this was working because you said it's big code. you don't blindly write hundereads of lines of code which does not work. and I know it was originally written for the VCL environment because of the use of the idantifreeze. if you imagine that you can just blindly move stuff from VCL environment to non-vcl enironment, you're way off track. in order to successfully do that you need to understand both environments, plus the relation between them. they are 2 totally different things. the vcl is asynchronous and the non-vcl is synchronous. it's like black and white.

you want us to solve your actual problem? you need to tell us exactly what the actual problem is. otherwise, you're on your own.
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
This video discusses moving either the default database or any database to a new volume.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now