Indy TCP Server freezing when deactivating - delphi

I have an Indy Server TIdTCPServer which has 3 bindings for different ports. If I connect a client to those 3 ports, and then deactivate the server, it gets stuck in what appears to be a deadlock. No matter what I do, it won't respond to my click, it won't even report "not responding" to Windows. If I disconnect the client(s) before deactivating the server, everything goes just perfect. I mean "deactivating" as in Server.Active:= False;.
Has anyone else experienced this? What might be causing it? I have nothing happening in here which crosses over threads which could in turn cause a deadlock (for example GUI updates). I tried an Antifreeze component TIdAntiFreeze but no luck.

TIdTCPServer is a multi-threaded component. A deadlock during server deactivation means that one or more of its client threads is not terminating correctly. That usually means that your server event handlers are doing something they should not be doing, typically either catching and discarding Indy's internal exceptions to itself, synchronizing with the thread context that is busy terminating the server, or deadlocking on something else outside of Indy. Without seeing your actual code, there is no way to know for sure which is actually the case, but it is always user error that causes this kind of deadlock.
TIdAntiFreeze only affects Indy components that run in the context of the main thread. TIdTCPServer does not.

I added this code on Form.OnClose works good!
procedure TformSFTP.FormClose(Sender: TObject; var Action: TCloseAction);
var
iA : Integer;
Context: TidContext;
begin
if sftpServidorFTP.Active then
with sftpServidorFTP.Contexts.LockList do
try
for iA := Count - 1 downto 0 do
begin
Context := Items[iA];
if Context = nil then
Continue;
Context.Connection.IOHandler.WriteBufferClear;
Context.Connection.IOHandler.InputBuffer.Clear;
Context.Connection.IOHandler.Close;
if Context.Connection.Connected then
Context.Connection.Disconnect;
end;
finally
sftpServidorFTP.Contexts.UnlockList;
end;
if sftpServidorFTP.Active then
sftpServidorFTP.Active := False;
end;

Related

How to signal a Indy TCPServer connection thread to terminate?

Before I shutdown the Indy TCPServer with Active:= False, I must signal all connections to abort what they doing and exit, because otherwise the server will be blocked and, with it, my application too.
From what I can think of, I need two things:
A way to send a Disconnect, because the thread may be blocked reading for commands.
A way to signal the thread to terminate, like Terminate; method of the TThread. I searched through server and I didn't found something similar. I know sending a Disconnect will throw an exception and the thread will exit, but maybe the thread is not reading and is busy doing something.
I tried this, but, of course, is not working and it corrupts my application and the system close it...
procedure TMyTcpServer.StopServer;
var List: TIdContextList;
I: Integer;
begin
List:= Contexts.LockList;
for I:= 0 to List.Count -1 do
TIdContext(List.Items[I]).Connection.Disconnect(False);
Contexts.UnlockList;
Active:= False;
end;

Delphi 7, Windows 7, event handler, re-entrent code

I've got some very old code (15+yr) that used to run ok, on older slower machines with older software versions. It doesn't work so well now because if fails a race condition. This is a general question: tell me why I should have known and expected the failure in this code, so that I can recognise the pattern in other code:
procedure TMainform.portset(iComNumber:word);
begin
windows.outputdebugstring(pchar('portset ' + inttostr(icomnumber)));
with mainform.comport do
try
if open then open := False; // close port
comnumber:=iComNumber;
baud:=baudrate[baudbox.itemindex];
parity:=pNone;
databits:=8;
stopbits:=1;
open:=true;
flushinbuffer;
flushoutbuffer;
if open then mainform.statusb.Panels[5].text:=st[1,langnum] {Port open}
else mainform.statusb.Panels[5].text:=st[2,langnum]; {port set OK}
except
on E: exception do begin
windows.OutputDebugString('exception in portset');
mainform.statusb.Panels[5].text:=st[3,langnum];
beep;
beep;
end;
end;
windows.outputdebugstring('portset exit');
end;
Note that flushinbuffer is protected with EnterCriticalSection(); AFAIK Nothing else is protected, and AFAIK there are no message handling sections. BUT
When this code is called from a click event, it gets part way through, then is interupted by a paint event.
The only tracing I have done is with outputdebugstring. I can see the first string repeated on entry before the second string is shown on exit. Is that real, or is it an illusion?
The trace looks like this:
4.2595 [4680] graph form click event
4.2602 [4680] portset 1 'from click event handler'
4.2606 [4680] graph form paint event
4.2608 [4680] portset 1 'from paint event handler'
4.2609 [4680] portset exit
4.3373 [4680] portset exit
This is a race condition: The paint event handler of the form is called before the click event handler code finishes, which causes failures. Serial code is AsyncPro. No thread code. Yes, there is more code, no it doesn't do anything in particular before "portset 1" but it does write to a form before it gets there:
with graphform do begin
if not waitlab.Visible then begin
waitlab.visible:=true;
waitprogress.position:=0;
waitprogress.visible:=true;
waitprogress.max:=214;
end;
end;
mainform.Statusb.panels[5].text:=gcap[10,langnum];
Don't hold back: What is it doing wrong, what should I be looking for?
This is expected behaviour - opening or closing a TApdComPort will service the message queue, specifically by calling a function it names SafeYield:
function SafeYield : LongInt;
{-Allow other processes a chance to run}
var
Msg : TMsg;
begin
SafeYield := 0;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
if Msg.Message = wm_Quit then
{Re-post quit message so main message loop will terminate}
PostQuitMessage(Msg.WParam)
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
{Return message so caller can act on message if necessary}
SafeYield := MAKELONG(Msg.Message, Msg.hwnd);
end;
end;
The TApdComPort is an async component - the com port is managed on background threads and opening or closing the port requires either starting or signaling those threads to stop. While waiting for them to free the component services the message queue in case it takes some time for things to synchronize (for example) :
if Assigned(ComThread) then
begin
{Force the comm thread to wake...}
FSerialEvent.SetEvent;
{... and wait for it to die}
ResetEvent(GeneralEvent);
while (ComThread <> nil) do
SafeYield;
end;
You haven't really show us enough of your own code to say why this is problematic in your case, however. I think David's point about com ports being manipulated in a paint handler is valid... we need to see the broader picture and what, exactly, the problem is that you are having.
A standard paint event cannot happen on its own, it can only be triggered by message retrieval. So the only way the code you showed could be interrupted the way you describe is if either the Serial component itself, or an event handler you have assigned to it, is doing something that pumps the calling thread's message queue for new messages.
Since you are closing the port in the beginning of your event handler, if there is any chance of triggering the event twice (i.e. by calling Application.ProcessMessages anywhere from your code, or calling TMainform.portset() directly from a worker thread), the new instance will close your port while the older one tries to communicate trough it, which will result in an error. AFAIS there are two solutions:
The faster but least bearable one is to protect your entire function with a Mutex (or event which is not a syncronisation object but can be used as one), but this only hides the coding error you have made.
The more pro solution is to find where the race condition gets raised, then fix your code. You can do it by searching all references to Application.ProcessMessages() and TMainform.portset(), and make sure that they won't get called paralelly. If no reference can be found on either mentioned function, the problem could still be caused by running multiple instances of your code ('cause it will not create multiple com ports :) ).
Remy Lebeau gets the credit for answering the question, because, as I asked for, it was a general reply to a general question. But it would have been inadequate without his comments in response to Uwe Raabe.
And what conclusively demonstrated that Remy Lebeau was correct was the exceptional answer from J, pointing out the specific point where the code failed.
Thanks also to David Heffernan for asking "why does code that responds to WM_PAINT call portset", which also makes a general point. And yes, the quick fix was just to block the path from the paint event handler to the comms code, but I'd done that without recognising the more general point.
I'll be having a look at the comms code, to see if there are more problems like this, and I'll be looking at the event handlers, to see if there are more problems like this, so thanks to everyone who read and considered the question.

Should I use a mutex for TCP client send or code my own queuing mechanism?

Delphi XE2, Indy V10, Windows 7 Pro - but I think I have a general conceptual problem.
Indy's TCP client is synchronous - it uses blocking calls.
However parts of my application are asynchronous - I want to send data over TCP and wait for a response when A) the 3rd party serial port component reports input from the serial port (it appears to be asynchronous & run in it's own thread, posting messages to my application's main form's Windows message queue) and B) when one of several timers expires (also asynchronous)
My application's handling of these async events needs to make a blocking call to send data over TCP and get a response before the next TCP data can be sent. E.G.
procedure OnSerialPortRxChar(...);
begin
if SendTCpData(...) = 'OK' then ...
end;
procedure OnTimerExpiry(...);
begin
if SendTCpData(...) = 'OK' then ...
end;
These should not interrupt each other, but currently do.
Obviously, my function SendTCpData needs some sort of blocking mechanism to prevent reentrant calls, or a queuing mechanism. Given that the caller needs to know the result, is my best solution a mutex? The problem is that the TCP transaction is just one line in the 20 line SendTCpData function which those asynch events can invoke.
I hope that I have explained this comprehendably. If not, please request more information.
Thank you very much in advance for your help.
If your serial library is AsyncPro, I would go with a single-threaded solution. There is more than one way to skin a cat, and I am not saying that you could not use a multi-threaded solution. But given that the AsycPro events will be running in the main thread, and you are not currently using threading in your application, this may be the simplest way forward.
In this solution we use a re-entry gate and a queue. The following is a mix of Delphi and pseudo-code.
var
isInTCP: boolean = False;
function DoSendTCP: boolean;
begin
isInTCP := True;
try
result := SendTCpData(...) = 'OK'
finally
isInTCP := False
end
end;
procedure OnSerialPortRxChar(...);
// This is safely re-entrant.
begin
repeat
if isInTCP then
Push the event onto a queue
else if DoSendTCP then
// Calling DoSendTCP may cause re-entry.
etc...
;
if (queue is empty) or isInTCP then break;
Pop from head of queue
until False
end;
You can use TIdAntiFreeze. Just drop it on your main form. Your call will still be blocked but your GUI will not be blocked. You may want to use some timeouts with your client though.

Closing a Client thread gracefully

I am troubleshooting a Delphi 7 Indy9 polling client. I have tried adding a TEvent with a waitforsingleobject and many other ways to disconnect gracefully. The error occurs in the readln. The error is usually an 'EIDConnection...not connected'. I have put a watch on it and the thread terminates. but the 'while' doesn't reevaluate the condition until the connection receives a msg from the server, so it just grinds at the readln until it receives a msg. So sometimes it disconnects gracefully but most times crashes. Is there a way to do this or do I just put a try...except around the readln and carry on...thanks in advance
procedure TReadingThread.Execute;
begin
while not Terminated and FConn.Connected do
begin
// read msg from server
Msg := FConn.ReadLn;
Synchronize(ReceiveLine);
end;
end;
I think you need to add some code to handle the Disconnect event. I had a similar problem to what you describe, and here's what I did (in this example, tcpServer is an instance of TIdTCPServer):
procedure TformRemoteControlWnd.tcpServerDisconnect(AContext: TIdContext);
(*
Connection is disconnected. Be careful, because this also gets called when
the app is shutting down while a connection is active, in which case
tcpServer may be gone already.
*)
begin
if not Application.Terminated and Assigned(tcpServer) then
begin
sbarStatus.SimpleText := 'TCP/IP Disconnected';
tcpServer.Tag := 0; // used to prevent rentrancy
end;
// shut down connection to stop thread from calling OnExecute event
try
AContext.Connection.Disconnect;
except
Sleep(0);
end;
end;
I have found the answer...Readln will wait indefinitely until it receives a carriage return. So the Thread sits at Readln until the server sends a message or the socket is disconnected (which causes the crash). In the Delphi compiler code, a comment was written in the OnDisconnect to trap the error using a try...except. So I just need to be careful to clean up before disconnecting the socket. I thought I could find a cleaner close method. Thanks for all the help.

delphi - terminate all the threads (TThread) on closing application

My application is a tcp/ip server, with main thread created only once & listening all the time. When new client connects, the main thread creates the new thread of TClientThread type. There is however no list of running Client threads, as that would make my app a bit complicated... is there any way to execute "terminate" method on all the threads, even if the thread is busy (in my case "busy" means it's waiting for the data, where the timeout set is about 30 sec ... so I have to kill it anyway, without waiting.)?
The simple closing application seems not to run "terminate" method on the threads, which ends up with memory leaks reported by FastMM...
Memory leaks on shutdown are nothing to worry about - going to the trouble of freeing memory before returning control to the operating system is a waste of time and needlessly slows down application exit. All you really need to do is ensure that all data has been saved, and all interprocess handles (such as semaphores and mutexes) correctly released, and exit away.
For notifying clients, the best you can do would be a strategy somewhat like this:
Add all client-handling threads to some list somewhere (with suitable locking on creation, destruction and iteration)
Make client threads remove themselves from the list upon termination, and have the last item removed from the list set an event (manual reset event, e.g. TEvent in SyncObjs) if the server is shutting down
Introduce polling (e.g. select or equivalent with a timeout) or other kind of interruption (e.g. SO_RCVTIMEO / SO_SNDTIMEO) in what would otherwise be long-running blocking routines, monitoring the Terminated property
On shutdown, lock the list and iterate through it, calling Terminate, and then wait for the event to be signaled; of course, the listening socket which adds items to the list should be closed and known to be closed before iterating through the list
Sounds like this article may help
What you'll see if you click that link:
Using Semaphores in Delphi, Part 2:
The Connection Pool
By: Cary Jensen
Abstract: Semaphores are used to
coordinate multiple threads and
processes. That semaphores provide
multiple threads with simultaneous
access to a shared resource is
highlighted by the
TFixedConnectionPool class described
in this article.
I use a KillThreadList: TList global.
I monitor it in my thread as:
while (Not Terminated) do
begin
inc(Inker);
if (WaitForSingleObject(FTick, finterval) = WAIT_TIMEOUT) then
Begin
if Inker >= 10 then
Begin
ProcessTables;
Inker := 0;
sleep(1000);
End;
if KillThreadList.Contains(ThreadID) = True then Terminate;
End;
end;
I also test for the KillThreadList in my processes to let me opt out of them before completion, where safe to do so.
I pass the OnTerminate event out to the Main thread and remove the ThreadID from the KillList there. I use this model extensively and it has not failed me yet.
procedure TfrmProcessQualcommLocations.OnTerminateThread;
var
ThreadID : Cardinal;
i : integer;
aStatusBar :TStatFrame;
begin
ThreadID := (Sender as Tthread).ThreadID;
for i := 0 to StatusBarList.Count -1 do
Begin
if StatusBarList.Items[i].ThreadID = ThreadID then
Begin
aStatusBar := StatusBarList.Items[i];
KillThreadList.Extract(ThreadID);
StatusBarList.Extract(aStatusBar);
aStatusBar.Free;
break;
End;
End;
self.Refresh;
end;
In the case above, I am also removing some GUI stuff.
Hope that helps.
SpringerRider

Resources