Search code examples
multithreadingdelphidelphi-2010indy

Delphi 2010 - Thread error: The handle is invalid (6)


I've created a thread that checks if a port is opened or closed. After running 753 times i get "Thread error: The handle is invalid (6)". What am I doing wrong? I've tested it against 50 ports and haven't had a problem. Now I try to check 65536 ports on my local ip an I get this error after the 731 port. Thank you!

Here is the thread code:

   {==============================START THREAD VERIFICARE PORT PRIN EXTRAGERE NUME HOST,IP,PORT============================================}



function elimina_toate_spatiile_goale(const s: string): string;
var
  i, j: Integer;
begin
  SetLength(Result, Length(s));
  j := 0;
  for i := 1 to Length(s) do begin
    if not TCharacter.IsWhiteSpace(s[i]) then begin
      inc(j);
      Result[j] := s[i];
    end;
  end;
  SetLength(Result, j);
end;


function Parse2(str: String; delimiter: Char; param_num: Integer): String;
var
  c, x, y : LongInt;
begin
  x := 1;   // param number that we're currently 'in'
  y := 0;   // position of previous delimiter
  for c := 1 to Length(str) do
    if str[c] = delimiter then  // this char is a delimiter
    begin
      if x = param_num then
        Break;
      inc(x);
      y := c;
    end;
  if x = param_num then
    Result := Copy(str, y + 1, c - y - 1)
  else
    Result := '';
end;





    type
      TThread_extrage_hostname_ip_si_port = class(TThread)
      private
        fStringul_ce_trebuie_parsat: string;
        fHostname,fIP_de_verificat,fPort_de_verificat,fIP_si_port:string;
        REZULTAT_verificare_port:BOOLEAN;
        flistbox_porturi_online,flistbox_porturi_offline:Tlistbox;
        fmemo_loguri:Tmemo;
       procedure semnalizare_port_online_sau_offline;

      protected
        procedure Execute; override;
      public

        constructor Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
      end;



    constructor TThread_extrage_hostname_ip_si_port.Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
    begin
    inherited Create(False);
      freeonterminate:=true;
      fStringul_ce_trebuie_parsat:=aStringul_ce_trebuie_parsat;
      flistbox_porturi_online:= alistbox_porturi_online;
      flistbox_porturi_offline:= alistbox_porturi_offline;
      fmemo_loguri:= amemo_loguri;
    end;

    procedure TThread_extrage_hostname_ip_si_port.Execute;
    var
      IdTCPClient : TIdTCPClient;
    begin

      // use fURL, fMethod, and fParam as needed...
    REZULTAT_verificare_port := False;


    fhostname:=(Parse2(fStringul_ce_trebuie_parsat,'=', 1));


    fIP_si_port:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,'=', 2));



    fIP_de_verificat:=elimina_toate_spatiile_goale(Parse2(fIP_si_port,':', 1));



    fPort_de_verificat:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,':', 2)) ;



    if (fIP_de_verificat<>'') and (fport_de_verificat<>'') then begin

      try
        IdTCPClient := TIdTCPClient.Create(nil);
        try
          IdTCPClient.Host := fIP_de_verificat;
          IdTCPClient.Port := strtoint(fPORT_de_verificat);
          IdTCPClient.ConnectTimeout:=5000;
          IdTCPClient.Connect;
          REZULTAT_verificare_port := True;
        finally
          IdTCPClient.Free;

        end;
      except
        //Ignore exceptions
      end;
     Synchronize(semnalizare_port_online_sau_offline);
    end;
    end;



    procedure TThread_extrage_hostname_ip_si_port.semnalizare_port_online_sau_offline;
    begin
    if REZULTAT_verificare_port=true then
    begin
    {verific daca in listbox-ul pentru porturi online exista deja elementul testat.
    Daca elementul nu exista, il adaug.
    Daca exista deja, nu il mai adaug.
    }
    if fListBox_porturi_online.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
    begin
    fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => ACTIV');
    fListBox_porturi_online.Items.Add(fStringul_ce_trebuie_parsat);
    end;
    Form2.GroupBox_porturi_online.Caption:='PORTURI ONLINE: '+inttostr(fListBox_porturi_online.Items.Count);
    end
    else
    begin
    {verific daca in listbox-ul pentru porturi online exista deja elementul testat.
    Daca elementul nu exista, il adaug.
    Daca exista deja, nu il mai adaug.
    }
    if fListBox_porturi_offline.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
    begin
    fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => OPRIT');
    fListBox_porturi_offline.Items.Add(fStringul_ce_trebuie_parsat);
    end;
    end;
    Form2.GroupBox_porturi_offline.Caption:='PORTURI OFFLINE: '+inttostr(fListBox_porturi_offline.Items.Count);
    end;


            {==============================STOP THREAD EXTRAGERE NUME HOST,IP,PORT============================================}

Solution

  • A wild guess.

          // ........ CODE ABOVE REMOVED
          IdTCPClient.Connect;
          REZULTAT_verificare_port := True;
        finally
          if IdTCPClient.connected then  // ADD SOMETHING LIKE THIS SECTION ?
          begin
           IdTCPClient.IOHandler.InputBuffer.clear;
           IdTCPClient.Disconnect;  
          end;
          IdTCPClient.Free;
          // ........ CODE BELOW REMOVED
    

    If this fixes it then the problem is that a single process can only have around 731 open sockets at once using the APIs you are using.

    Alternatively maybe the system you are connecting to also has limits on the total number of open sockets (it can receive) simultaneously?

    Either way you are hit a system limit somewhere.

    ...

    UPDATE due to your comment.

    Threads have resource limits too. You can not create unlimited threads (like you can not create unlimited TCP sockets). Can you use a thread-safe counter and ensure you stop creating threads when you hit 100 threads, until one of the previous thread exit.

    • So the counter starts at 0.
    • Counter is checked to be less than 100, to allow creation of a thread.
    • If 100 or more, then it waits to be woken up (this is done by exiting threads)
    • Counter increments by 1 when a thread is created.
    • Ideally the check less than 100 and increment by one are done atomically.
    • As each thread exits the counter is decremented by 1. This must be done reliably, as in there maybe many ways (normal termination, exceptions thrown, etc..) for a thread to exit, you need to catch them all. When the counter is decremented you always wake up any waiters.

    Now you can create this yourself with a basic sleep()/wait()/synchronize() like in Java. You have a method that only returns when it increments (it will wait as needed, until it can increment) and another method that always decrements.

    Now you may find your main thread can not sleep for other reasons, some systems need to call API to reap the dead/exited threads, before the threads resources are really freed up. In this case you decrement by one when the resource is available, not at the point of the thread execution ended.

    Even 100 threads running your code will run very fast, you may find that if you limit the number of threads to between the number of core and double the number of cores in your system, that you can make the whole thing faster (if the tasks are CPU bound).

    Since 100 threads active on a 8 core system, is not as efficient as 16 threads active on an 8 core system. However a lot of your process time will be dominated by waiting for network connection delays. So 16 thread will not be enough.

    This pattern is often call a semaphore (with counter). Google hit for "delpi semaphore threading" http://edn.embarcadero.com/article/29908