I'm using telnet client in my app, to access streaming market data.
It works ok for Windows and iOS, but on Android the app hangs
for several seconds when telnet.Disconnect is called. So much that
Android detects the app is stalled and terminates it.
I understand that Connect and Disconnect can block the
execution, so they are not to be called from the UI thread.
But why Disconnect takes so long ?
One guy suggested on stackoverflow that the reader thread
was locked or not finishing, and that blocked the Disconnect call.
So I tried to create a thread dedicated to disconnect.
It seems to work
Code:
procedure TForm1.ThreadedTelnetDisconnect;
var aThreadDisconnect:TThread;
begin
aThreadDisconnect := TThread.CreateAnonymousThread(
procedure begin
IdTelnet1.Disconnect;
end);
aThreadDisconnect.start();
end;
I cant speak for Android, but on Windows and others it is valid to disconnect from a different thread and it is a common practice.
(11-15-2019, 01:03 PM)omarreis Wrote: [ -> ]I understand that Connect and Disconnect can block the
execution, so they are not to be called from the UI thread.
But why Disconnect takes so long.
Probably because
TIdTelnet runs an internal reading thread, and
Disconnect() waits for that thread to fully terminate after closing the socket behind the thread's back.
(11-15-2019, 01:03 PM)omarreis Wrote: [ -> ]One guy suggested on stackoverflow that the reader thread
was locked or not finishing, and that blocked the Disconnect call.
Very likely.
(11-15-2019, 01:03 PM)omarreis Wrote: [ -> ]So I tried to create a thread dedicated to disconnect.
It seems to work
Rather than doing a full
Disconnect() in a worker thread, I would probably just
Close() the
IOHandler to close the underlying socket, let the reading thread detect the closure (ie, a read failure) and terminate itself, and then have the main thread detect that (or have the worker signal the main thread) to
Disconnect() the client to free up the thread.
(11-15-2019, 06:49 PM)kudzu Wrote: [ -> ]I cant speak for Android, but on Windows and others it is valid to disconnect from a different thread and it is a common practice.
Android is Java on top of Linux. On Linux, it is valid to
close() a socket in a different thread, provided you
shutdown() the socket first (which Indy does) to abort any operations in progress. On non-Windows platforms, simply
close()'ing a socket does not guarantee that operations in progress are aborted, especially thread-blocking operations.
It is also not advisable to close sockets across thread boundaries due to file descriptor reuse (which doesn't affect Windows). As soon as the socket is closed, its descriptor can be reused for a completely new file/socket, which can affect threads that were still holding the original descriptor and don't realize the socket is gone. Which, sadly, perfectly describes what
TIdTelnet actually does internally. A safer closure design involves using a separate pipe to signal threads to stop their socket operations before the socket is then closed. But Indy does not do that.
(11-16-2019, 07:54 PM)rlebeau Wrote: [ -> ]Rather than doing a full Disconnect() in a worker thread, I would probably just Close() the IOHandler to close the underlying socket, let the reading thread detect the closure (ie, a read failure) and terminate itself, and then have the main thread detect that (or have the worker signal the main thread) to Disconnect() the client to free up the thread.
I tried:
IdTelnet1.IOHandler.Close;
instead of:
IdTelnet1.Disconnect;
(calls from the UI thread)
That fixed the app blocking problem, but the component no longer called IdTelnet1.Disconnected event, which I use.
The threaded disconnect solution above seems to work better.
Thanks
(11-18-2019, 02:18 PM)omarreis Wrote: [ -> ]I tried:
IdTelnet1.IOHandler.Close;
instead of:
IdTelnet1.Disconnect;
(calls from the UI thread)
That fixed the app blocking problem, but the component no longer called IdTelnet1.Disconnected event, which I use.
You shouldn't be using the
OnDisconnected event to begin with. Indy clients are not event driven. The
OnDisconnected is only called when
Disconnect() is called, so you could have (and should have) simply run your
OnDisconnected logic right after calling
Disconnect().
In any case, if you really need an event, have a look at the
OnStatus event, it has an
hsDisconnected notification.