I'm using Windows 10 1903, Delphi Rio 10.3.3 with Indy 10.6.2.5366.
I'm upgrading an old Windows Service application that used TServerSocket to Indys TIdTCPServer. The main reason is that I have to add IPv6 support to the application.
The problem I have is that the TIdTCPServer eats threads. For every connection to the TIdTCPServer a new thread is allocated (as it should) but when the client disconnects the thread doesn't close.
If I connect two clients simultaneously, IdTCPServer1.Contexts.LockList.Count = 2 and the OnExecute event are fired twice every "cycle" as it should. If I then disconnect one client so IdTCPServer1.Contexts.LockList.Count = 1, the OnExecute are fired only once every cycle (also as it should), but both threads are still running. (I use Windows taskmanager to see this)
I built a simple test-application that connects and disconnects randomly with a few seconds interval, started 5 programs and my Service crashes when reaching approximately 150 threads.
Here is my OnExecute event:
procedure T#############.IdTCPServer1Execute(AContext: TIdContext);
var
incomestring: string;
begin
try
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
Exit;
end;
incomestring:= AContext.Connection.IOHandler.InputBufferAsString(en8bit);
if length(trim(incomestring)) = 0 then
begin
exit;
end;
DataRead(incomestring, AContext); // Send data be processed
except
on E: Exception do
begin
if E is EIdException then
raise // <- re-raise only Indy-specific exceptions end;
else
begin
inc(ErrorCount);
Logg('Error in IdTCPServer1Execute: ' + E.Message, ltError, AContext);
end;
end;
end;
end;
The reason I'm using InputBufferAsString is that I can't control the clients connecting to the server. So there isn't always CR or LF at the end (as ReadLn wants). And also the clients stay connected to the server for many hours before disconnecting.
T####
instead of just the defaultTForm1
? – Ken WhiteTIdSchedulerOfThreadPool
toTIdTCPServer.Scheduler
, otherwiseTIdSchedulerOfThreadDefault
is used instead, which doesn't pool. But are you really callingIdTCPServer1.Contexts.LockList()
, though? If so, are you callingIdTCPServer1.Contexts.UnlockList()
as well? I don't see anything in this code that would cause threads to not terminate properly, unless eitherDataRead()
orLogg()
are blocking threads, or you are keeping theContexts
locked. Something outside of the code shown. – Remy Lebeau