Search code examples
delphiindy

TidCustomTCPServer.Active := False, never returns


I downloaded some interesting code from Indy 10 TIdTCPServer and compiled and ran it. It's a bit messy and it probably needs work but it's a good foundation for something interesting. At least I don't have to post all the code here. Anyway, I decided that the ExampleServer needs to be de-activated first, not just freed in OnDestroy. So I added:

procedure TServerPushExampleForm.FormClose(Sender: TObject);
begin
  ExampleServer.Active := False; // bug here - never returns
end;

To try to debug this, I added IdCustomTCPServer.pas to the project and stepped into it.

An exception is raised in TIdCustomTCPServer.StopListening; at the line LListener.WaitFor; This exception is trapped in the exception handler in TIdListenerThread.Run; but the E.message isn't retrieved there, so I had to modify the code there to get the message:

"Operation Aborted"

I traced it further after that, but the code execution eventually comes back to the same exception handler.

Is this a bug or is it just better to never set the Active property to False? In my tests, if I close the app and let the RTL manage all the free'ing. The infinite loop doesn't occur (the app does actually close)


Solution

  • The exception is normal behavior.

    Indy uses blocking sockets.

    TIdListenerThread runs a loop waiting for clients to connect. Each wait is a blocking operation.

    When the server is being deactivated, it closes its listening socket(s), causing pending socket operations to abort, and then the listening thread(s) are terminated. The abort exception is handled internally.

    The server's destructor also sets Active=False, so whether you deactivate explicitly or not, the server will be deactivated.

    This, in if itself, is not causing your hang. Typically, the only way setting Active=False can ever hang is if a server event handler tries to sync with the main UI thread synchronously while the main thread is blocked waiting for the server to deactivate. Classic deadlock scenario.

    That does not appear to be the case in the demo you linked to, though (the only sync being used is asynchronous instead). So, something else is likely going on that your debugging hasn't reveiled yet. There should be no hang.