Board index » delphi » EIdClosedSocket and TidcmdTCPServer and Freeze on close

EIdClosedSocket and TidcmdTCPServer and Freeze on close


2006-09-23 02:46:22 AM
delphi6
I have a problem that my server freezes when I try and set cmdTCPServer.Active to false when I have
clients connected.
However, it only freezes when I try to add a message to a memo on the main form in the onDisconnect
event.
There is no problem when the onDisconnect event is called in the normal course of events (i.e. a
client disconnects), only when I try to set Active to false.
Here is the relevant code:
(FAdminFrm is the main form.)
===============
procedure TdmTCPServer.tcpServerDisconnect(AContext: TIdContext);
var
Client : TMyClient;
begin
{ Retrieve Client Record from Data pointer }
Client := Pointer(AContext.Data);
FAdminFrm.AddToLog('Disconnecting: '+Client.Name +
' : '+TimeTostr(Now) +
' Addr: '+Client.IPAddress);
{ Remove Client from the Clients TList }
FClients.Delete(Client.ListLink);
{ Free the Client object }
Client.Free;
AContext.Data := nil;
CoUnInitialize;
end;
procedure TfrmTCPServerAdmin.AddToLog(const msg: string);
begin
memLog.Lines.Add(msg); // Application hangs here!!!
end;
==================
When tracing into AddToLog msg does indeed contain the message I expect.
Clive.
Clive Walden
Walden Consulting
Phone: 760-632-5856
Web site: www.clivewalden.com
 
 

Re:EIdClosedSocket and TidcmdTCPServer and Freeze on close

"Clive Walden" <XXXX@XXXXX.COM>writes
Quote
I have a problem that my server freezes when I try and set
cmdTCPServer.Active to false when I have clients connected.
That is because your code is wrong. Keep reading.
Quote
However, it only freezes when I try to add a message to a
memo on the main form in the onDisconnect event.
The code you have shown is not thread-safe. You cannot access the UI of the
main thread from the context of a worker thread. You must Synchronize the
access to that the code accessing the UI is running in the context of the
main thread instead. You are not doing that.
There is another gotcha here. If you use the Synchronize() method of TThread
or TIdSync to access the UI, then you are going to deadlock your code when
shutting down the server. Setting the Active property to false internally
causes the server to wait until all clients have disconnected and all
threads have terminated before then exiting the property setter. If you set
the property in the context of the main thread, then the main thread will be
blocked and unable to process Synchronize() requests. Which in turn will
block the worker threads when they call Syncronize(), so they will be uable
to terminate, and the server will remain blocked.
Quote
There is no problem when the onDisconnect event is called in the normal
course of events
Yes there is, because you are not accessing the Memo in a safe manner.
Quote
Here is the relevant code:
Try this code instead:
type
TAddToLogNotify = class(TIdNotify)
protected
FMsg: String;
procedure DoNotify; override;
public
constructor Create(const AMsg: String); reintroduce;
class procedure NotifyMessage(const AMsg: String);
end;
constructor TAddToLogNotify.Create(const AMsg: String);
begin
inherited Create;
FMsg := AMsg;
end;
class procedure TAddToLogNotify.NotifyMessage(AMethod: TIdThreadMethod);
begin
TAddToLogNotify.Create(AMsg).Notify;
end;
procedure TAddToLogNotify.DoNotify;
begin
FAdminFrm.AddToLog(FMsg);
end;
procedure TdmTCPServer.tcpServerDisconnect(AContext: TIdContext);
var
Client : TMyClient;
begin
Client := Pointer(AContext.Data);
AContext.Data := nil;
TAddToLogNotify.NotifyMessage('Disconnecting: ' + Client.Name +' : '
+ TimeTostr(Now) + ' Addr: ' + Client.IPAddress);
FClients.Delete(Client.ListLink);
Client.Free;
CoUnInitialize;
end;
Quote
When tracing into AddToLog msg does indeed contain
the message I expect.
But you are trying to access the Memo in the context of the worker thread,
not the main thread. All sorts of things can go wrong with that.
Gambit
 

Re:EIdClosedSocket and TidcmdTCPServer and Freeze on close

Remy,
Thank you for your usual prompt and informative response.
You are very much appreciated!
I will test out your suggestion ASAP.
I suspected the issue was Synchronization; but was unsure how to address it, especially as neither
the Context nor the IOHandler seemed to have a Synchronize method.
Thanks again.
Clive.
Clive Walden
Walden Consulting
Phone: 760-632-5856
Web site: www.clivewalden.com
 

Re:EIdClosedSocket and TidcmdTCPServer and Freeze on close

A few questions issues now I have tried it<s>.
1. You have a different signature for NotifyMessage in the interface and implementation.
I changed the implementation to match the interface. I hope that was correct.
2. DoNotify.
I had to add the dataModule object instance in front of FAdminFrm.
3. The class method NotifyMessage creates an instance of
TAddToLogNotify; but I see no place where it is freed.
Does the ancestor class take care of that?
Thanks,
Clive.
Clive Walden
Walden Consulting
Phone: 760-632-5856
Web site: www.clivewalden.com
 

Re:EIdClosedSocket and TidcmdTCPServer and Freeze on close

"Clive Walden" <XXXX@XXXXX.COM>writes
Quote
1. You have a different signature for NotifyMessage in the interface and
implementation.
A typo on my part.
Quote
I had to add the dataModule object instance in front of FAdminFrm.
You did not say that FAdminFrm was a member of a class, so I did not use it
as one.
Quote
The class method NotifyMessage creates an instance of
TAddToLogNotify; but I see no place where it is freed.
The TIdNotify class uses an internal thread for triggering notifications.
That thread will free any TIdNotify objects that have been added to its
queue by the TIdNotify.Notify() method.
Gambit
 

Re:EIdClosedSocket and TidcmdTCPServer and Freeze on close

Thanks again.
It appears to be working flawlessly.
Clive.
Clive Walden
Walden Consulting
Phone: 760-632-5856
Web site: www.clivewalden.com