Delphi tIdTCPClient with timer events and other multi-threaded client side events - multithreading

We have a Delphi client server application using INDY. The client has a single tIdTCPClient connection to the server which is multi threaded. The client is "theoretically" a single thread. But in practice there are multiple threads on the client and this is where my problem is. For example think of a timer that fires every minute to get data from the server. And consider what happens when a user runs a command at the same time as this timer event. In truth, my problem is caused by our "Report Builder" reporting tool that (annoyingly) insists on loading every page of a report, which takes a while. The report runs off our "special" dataset that has a caching mechanism to transmit batches of records at a time (so multiple calls to the server to get all data). Meanwhile if a user does something else at the same time we seem to be getting crossed data. It seems the user getting back data that was meant for the report.
By the way, this bug is extremely rare, but a lot less rare for one particular customer who has the worlds slowest internet (luck me - I now have a test environment).
So on the client I have code a bit like this...
procedure DoCommand(MyIdTCPClient:tIdTCPClient; var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms'); //It bugs out here in rare cases
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize); //Report thread is stuck here
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
end;
Now when I debug it, I can confirm it bugs out when there are two threads in the middle of this procedure. The main thread stops at the exception. And the report thread is stuck somewhere else in the same procedure.
So, it looks to me like I need to make the procedure above thread safe.
I mean so if the user wants to do something they just have to wait until the report thread finishes.
Arrrgh, I thought my client application was single threaded for sending data to the server!
I think that using TThread would not work - because I don't have access to the thread inside Report Builder. I think I need a tCriticalSection.
I think I need to make the application so that the above procedure can only be run by one thread at a time. Other threads have to wait.
Someone please help with the syntax.

TIdIOHandler has Write() and Read...() overloads for sending/receiving TStream data:
procedure Write(AStream: TStream; ASize: TIdStreamSize = 0; AWriteByteCount: Boolean = False); overload; virtual;
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
You do not need to copy the TMemoryStream contents to an intermediate TIdBytes before sending it, or receive it as TIdBytes before copying it back to the TStream. In fact, there is nothing in in the code you have shown that needs to use TIdBytes directly at all:
procedure DoCommand(MyIdTCPClient: TIdTCPClient; var DATA: TMemoryStream);
var
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA, 0, True);
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
CommsVerTest := MyIdTCPClient.IOHandler.ReadString(6);
if CommsVerTest <> 'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DATA.Clear;
MyIdTCPClient.IOHandler.ReadStream(DATA, -1, False);
end;
With that said, if you have multiple threads writing to the same socket at the same time, or multiple threads reading from the same socket at the same time, they will corrupt each other's data (or worse). You need to synchronize access to the socket, such as with a critical section at a minimum. Because of your multi-threaded use of TIdTCPClient, you really need to re-think your overall client design.
At the very least, using your existing logic, when you need to send a command and read a response, stop the timer and wait for any pending data to be exchanged before then sending the command, and do not allow anything else to access the socket until the response comes back. You are trying to do too much at one time without synchronizing everything to avoid overlaps.
In the long run, it would be much safer to do all of the reading from a single dedicated thread and then pass any received data to your other threads for processing as needed. But that also means changing your sending logic to match. You could either:
If your protocol allows you to have multiple commands in flight in parallel, then you can send a command from any thread at any time (just be sure to use a critical section to avoid overlaps), but do not wait for a response immediately. Let each sending thread move on and do other things, and have the reading thread notify the appropriate sending thread asynchronously when the expected response actually arrives.
If the protocol does not allow for parallel commands, but you still need each sending thread to wait for its respective response, then give the socket thread a thread-safe queue that other threads can push commands into when needed. The socket thread can then run through that queue periodically sending each command and receiving its response one at a time as needed. Each thread that puts a command into the queue can include a TEvent to be signaled when the response arrives, that way they enter efficient sleep states while waiting, but you preserve your per-thread waiting logic.

Thanks Remy.
The TCriticalSection solved the problem. I have no control over things like the 3rd party report builder. And running reports entirely in their own thread wouldn't make much difference - they still need to share the same connection (I don't want or need parallel connections). Anyway the bulk of the program runs in the main thread, and it is rare that two threads need to communicate with the server at the same time.
So TCriticalSection was perfect - it prevented this procedure running twice at the same time (ie one thread has to wait until the first is finished). And happily - it worked brilliantly.
Basically the code now looks like this:
procedure DoCommand(
CS:tCriticalSection;
MyIdTCPClient:tIdTCPClient;
var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
CS.Enter; //enter Critical Section
try
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize);
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
finally
cs.Leave;
end;
end;

Related

How to make a bidirectional TCP connection using Indy?

I use TIdTCPClient and TIdTCPServer (with my own protocol) and I want to make a TCP connection in which both computers can send commands and read replays. Of course, one will be server and the other client, but just until the connection is established, after that, they both must be "servers and clients". I don't know how to explain this more clearly... Mainly, computer "A" will send commands to computer "B", but sometimes, some events happen on the computer "B" and must be communicated immediately to computer "A". So, every computer should listen all the time for commands, but in the same time it must be able to send some commands when the events are happening.
For sending commands it's simple: just write something to the socket. But to be able to replay them, the application must read from socket, and if it read, it can't write.... I searched the internet and I found here and here some answers to similar questions, in which is said that should be used 2 threads, one for writing and one for reading. But I don't understand how cand I use the same object, the connection socket, in 2 threads... What if one thread reads something that is changed by the other (the basic thread problem) ?
I have made some tests with a chat application with the following code and it seems it's working fine but I'm not sure if this is the correct way to do it... I mean, to create a connection and after that pass the socket object to two threads, one for read and one for write.
constructor TReadingThread.Create(ASocket: TIdIOHandlerSocket);
begin
FSocket := ASocket;
inherited Create(False);
end;
procedure TReadingThread.Execute;
var
cmd: string;
begin
while not Terminated do
begin
cmd := FSocket.ReadLn;
Trim(cmd);
if cmd <> '' then
Synchronize(
procedure
begin
Form1.Display.Lines.Add(cmd);
end);
end;
end;
procedure TForm1.BConnectClick(Sender: TObject);
begin
TCPClient.Connect;
end;
procedure TForm1.InputKeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13 then begin
TCPClient.Socket.WriteLn(Input.Text);
Input.Clear;
Key:=#0;
end;
end;
procedure TForm1.TCPClientConnected(Sender: TObject);
begin
readthread:= TReadingThread.Create(TCPClient.Socket);
Display.Lines.Add('Server: Connected.');
end;
computer "A" will send commands to computer "B", but sometimes, some events happen on the computer "B" and must be communicated immediately to computer "A". So, every computer should listen all the time for commands, but in the same time it must be able to send some commands when the events are happening.
When A sends a command to B, or vice versa, it has no way of knowing whether the next message received will be a response to the command or an unsolicited event. So it can't just send a command and sit waiting for the response, as it may receive other messages in the meantime. You will have to read messages asynchronously and handle them on a per-message basis as they arrive.
To help facilitate this, every message must identify what it is (command, response, or event). When a command is sent, the sender should include a user-defined value in it that is echoed back in the response. This will allow the sender to correlate response to commands, and even to have multiple commands in flight at the same time. Unsolicited events would not have a user-defined value in them, as they don't get a response.
This way, events can then be sent at any time, and when a command is received it can be processed in parallel to anything else, and a response sent back whenever it is ready (even out of order of other commands, if desired).
For sending commands it's simple: just write something to the socket. But to be able to replay them, the application must read from socket, and if it read, it can't write....
Yes, it can. You just can't do it in the same thread, if the thread is blocked doing other things.
I searched the internet and I found here and here some answers to similar questions, in which is said that should be used 2 threads, one for writing and one for reading.
Yes, that is one solution, and the one you will likely end up using with Indy, given its blocking nature. But it is not the only solution when working with sockets in general, there are better solutions (overlapped I/O, etc).
But I don't understand how cand I use the same object, the connection socket, in 2 threads...
What makes you think it can't be shared? A socket has separate inbound and outbound buffers. One thread can be reading inbound data while another thread is writing outbound data at the same time. They will not interfere with each other.
What if one thread reads something that is changed by the other (the basic thread problem) ?
Nothing is being changed on the reading side while the writing side is working, and vice versa.
What you have to watch out for is multiple threads reading from the socket at the same time, or multiple threads writing to the socket at the same time. That is not safe without adequate synchronization between the threads. But one thread reading while one thread is writing is perfectly safe without synchronizing the two threads.
I have made some tests with a chat application with the following code and it seems it's working fine but I'm not sure if this is the correct way to do it... I mean, to create a connection and after that pass the socket object to two threads, one for read and one for write.
That is perfectly fine. Just make sure the reading thread is terminated before the connection object is destroyed.

Post message from Thread to GUI best practice?

I am working on small monitoring application which will have some threads for communication with some devices via SNMP, TCP, ICMP, other threads have to perform some calculations.
All this result I have to output in GUI (some Forms or TabSheets).
I am thinking about next possibilities:
use Synchronize from every worker thread:
use shared buffer and windows messaging mechanism. Thread will put message in shared buffer (queue) and will notify GUI with windows message.
use separate thread which will listen for Synchronization primitives (Events, Semaphores, etc) and use again Synchronize, but only from GUI-dedicated thread only, or Critical Section on GUI to display message.
UPDATE: (Proposed by one workmate) use shared buffer and TTimer in main form which will check periodically (100-1000 ms) shared buffer and consuming, instead of windows messaging. (Does it have some benefit over messaging?)
Other?
Dear experts, please explain what is the best practice or what are the advantages and disadvantages of exposed alternatives.
UPDATE:
As idea:
//shared buffer + send message variant
LogEvent global function will be called from everywhere (from worker threads too):
procedure LogEvent(S: String);
var
liEvent: IEventMsg;
begin
liEvent := TEventMsg.Create; //Interfaced object
with liEvent do
begin
Severity := llDebug;
EventType := 'General';
Source := 'Application';
Description := S;
end;
MainForm.AddEvent(liEvent); //Invoke main form directly
end;
In Main Form, where Events ListView and shared section (fEventList: TTInterfaceList which is already thread-safe) we'll be:
procedure TMainForm.AddEvent(aEvt: IEventMsg);
begin
fEventList.Add(aEvt);
PostMessage(Self.Handle, WM_EVENT_ADDED, 0, 0);
end;
Message handler:
procedure WMEventAdded(var Message: TMessage); message WM_EVENT_ADDED;
...
procedure TMainForm.WMEventAdded(var Message: TMessage);
var
liEvt: IEventMsg;
ListItem: TListItem;
begin
fEventList.Lock;
try
while fEventList.Count > 0 do
begin
liEvt := IEventMsg(fEventList.First);
fEventList.Delete(0);
with lvEvents do //TListView
begin
ListItem := Items.Add;
ListItem.Caption := SeverityNames[liEvt.Severity];
ListItem.SubItems.Add(DateTimeToStr(now));
ListItem.SubItems.Add(liEvt.EventType);
ListItem.SubItems.Add(liEvt.Source);
ListItem.SubItems.Add(liEvt.Description);
end;
end;
finally
fEventList.UnLock;
end;
end;
Is there something bad? Main Form is allocated ONCE on application startup and Destroyed on application exit.
Use Synchronize from every worker thread
This would probably be the simplest approach to implement, but as others have indicated will lead to your IO threads being blocked. This may/may not be a problem in your particular application.
However it should be noted that there are other reasons to avoid blocking. Blocking can make performance profiling a little trickier because it effectively pushes up the time spent in routines that are "hurrying up and waiting".
Use shared buffer and windows messaging mechanism
This is a good approach with a few special considerations.
If your data is extremely small, PostMessage can pack it all into the parameters of the message making it ideal.
However, since you make mention of a shared buffer, it seems you might have a bit more data. This is where you have to be a little careful. Using a "shared buffer" in the intuitive sense can expose you to race conditions (but I'll cover this in more detail later).
The better approach is to create a message object and pass ownership of the object to the GUI.
Create a new object containing all the details required for the GUI to update.
Pass the reference to this object via the additional parameters in PostMessage.
When the GUI finishes processing the message it is responsible for destroying it.
This neatly avoids the race conditions.
WARNING: You need to be certain the GUI gets all your messages, otherwise you will have memory leaks. You must check the return value of PostMessage to confirm it was actually sent, and you may as well destroy the object if not sent.
This approach works quite well if the data can be sent in light-weight objects.
Use separate thread ...
Using any kind of separate intermediate thread still requires similar considerations for getting the relevant data to the new thread - which then still has to be passed to the GUI in some way. This would probably only make sense if your application needs to perform aggreagation and time-consuming calculations before updating the GUI. In the same way that you don't want to block your IO threads, you don't want to block your GUI thread.
Use shared buffer and TTimer in main form
I mentioned earlier that the "intuitive idea" of a shared buffer, meaning: "different threads reading and writing at the same time"; exposes you to the risk of race conditions. If in the middle of a write operation you start reading data, then you risk reading data in an inconsistent state. These problems can be a nightmare to debug.
In order to avoid these race conditions you need to fall back on other synchronisation tools such as locks to protect the shared data. Locks of course bring us back to the blocking issues, albeit in a slightly better form. This is because you can control the granularity of the protection desired.
This does have some benefits over messaging:
If your data structures are large and complex, your messages might be inefficient.
You won't need to define a rigorous messaging protocol to cover all update scenarios.
The messaging approach can lead to a duplication of data within the system because the GUI keeps its own copy of the data to avoid race conditions.
There is a way to improve the idea of shared data, only if applicable: Some situations give you the option of using immutable data structures. That is: data structures that do not change after they've been created. (NOTE: The message objects mentioned earlier should be immutable.) The benefit of this is that you can safely read the data (from any number of threads) without any synchronisation primitives - provided you can guarantee the data doesn't change.
The best approach is to use a GDI custom message and just call PostMessage() to notify the GUI.
type
TMyForm = class(TForm)
.
.
.
private
procedure OnMyMessage(var Msg: TMessage); message WM_MY_MESSAGE;
procedure OnAnoMessage(var Msg: TMessage); message WM_ANO_MESSAGE;
.
.
PostMessage(self.Handle,WM_MY_MESSAGE,0,0);
See this great article for full explanation.
This is a lighter/faster approach to rely on the OS internal features.

Periodical tasks in Delphi app

I'm developing an application that should perform certain sql queries in different MSSQL servers after specified intervals. My thoughts are about make an array of threads and in each thread run timer in which task will run. Does it really needs to make each thread for each timer, or should I just distinguish one timer for each task (whatever timer will produce thread)? Any thoughts about implementation?
Thanks a lot, guys!
I doubt that you need to have one thread per task. It would probably suffice to create one timer per task. If a timer fires whilst another task is running then the second task will have to queue up but it doesn't sound like that will be a great problem.
If you are going to use a Delphi TTimer to do this you'll need to make sure that your service has a message queue and runs a message loop on that queue. You may wish to run that message queue on a separate thread but if you do make sure that the TTimer objects are created on that thread so that they are associated with the right message queue.
You ask in the comments how to run a message loop in a thread. The following code should suffice:
repeat
try
Application.HandleMessage;
except
Application.HandleException(Application);
end;
until Terminated;//this is the Terminated property of the thread
This will give you all the bells and whistles of the Delphi message loop. If you want a very standard message loop you can use this:
procedure PerformThreadLoop;
var
Msg: TMsg;
begin
repeat
Try
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
WaitMessage;
Except
Application.HandleException(Self);
End;
until Terminated;//this is the Terminated property of the thread
end;
If all you want is to pump WM_TIMER messages both will work but I personally would be inclined to go with the second option, the raw Win32 API version.

Delphi - Cross-thread event handling

I have a small client-server application, where server sends some messages to the client using named pipes. The client has two threads - main GUI thread and one "receiving thread", that keeps receiving the messages sent by server via the named pipe. Now whenever some message is received, I'd like to fire a custom event - however, that event should be handled not on the calling thread, but on the main GUI thread - and I don't know how to do it (and whether it's even possible).
Here's what I have so far:
tMyMessage = record
mode: byte;
//...some other fields...
end;
TMsgRcvdEvent = procedure(Sender: TObject; Msg: tMyMessage) of object;
TReceivingThread = class(TThread)
private
FOnMsgRcvd: TMsgRcvdEvent;
//...some other members, not important here...
protected
procedure MsgRcvd(Msg: tMyMessage); dynamic;
procedure Execute; override;
public
property OnMsgRcvd: TMsgRcvdEvent read FOnMsgRcvd write FOnMsgRcvd;
//...some other methods, not important here...
end;
procedure TReceivingThread.MsgRcvd(Msg: tMyMessage);
begin
if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, Msg);
end;
procedure TReceivingThread.Execute;
var Msg: tMyMessage
begin
//.....
while not Terminated do begin //main thread loop
//.....
if (msgReceived) then begin
//message was received and now is contained in Msg variable
//fire OnMsgRcvdEvent and pass it the received message as parameter
MsgRcvd(Msg);
end;
//.....
end; //end main thread loop
//.....
end;
Now I'd like to be able to create event handler as member of TForm1 class, for example
procedure TForm1.MessageReceived(Sender: TObject; Msg: tMyMessage);
begin
//some code
end;
that wouldn't be executed in the receiving thread, but in main UI thread. I'd especially like the receiving thread to just fire the event and continue in the execution without waiting for the return of event handler method (basically I'd need something like .NET Control.BeginInvoke method)
I'm really beginner at this (I tried to learn how to define custom events just few hours ago.), so I don't know if it's even possible or if I'm doing something wrong, so thanks a lot in advance for your help.
You've had some answers already, but none of them mentioned the troubling part of your question:
tMyMessage = record
mode: byte;
//...some other fields...
end;
Please take note that you can't do all the things you may take for granted in a .NET environment when you use Delphi or some other wrapper for native Windows message handling. You may expect to be able to pass random data structures to an event handler, but that won't work. The reason is the need for memory management.
In .NET you can be sure that data structures that are no longer referenced from anywhere will be disposed off by the garbage collection. In Delphi you don't have the same kind of leeway, you will need to make sure that any allocated block of memory is also freed correctly.
In Windows a message receiver is either a window handle (a HWND) which you SendMessage() or PostMessage() to, or it is a thread which you PostThreadMessage() to. In both cases a message can carry only two data members, which are both of machine word width, the first of type WPARAM, the second of type LPARAM). You can not simply send or post any random record as a message parameter.
All the message record types Delphi uses have basically the same structure, which maps to the data size limitation above.
If you want to send data to another thread which consists of more than two 32 bit sized variables, then things get tricky. Due to the size limits of the values that can be sent you may not be able to send the whole record, but only its address. To do that you would dynamically allocate a data structure in the sending thread, pass the address as one of the message parameters, and reinterpret the same parameter in the receiving thread as the address of a variable with the same type, then consume the data in the record, and free the dynamically allocated memory structure.
So depending on the amount of data you need to send to your event handler you may need to change your tMyMessage record. This can be made to work, but it's more difficult than necessary because type checking is not available for your event data.
I'd suggest to tackle this a bit differently. You know what data you need to pass from the worker threads to the GUI thread. Simply create a queueing data structure that you put your event parameter data into instead of sending them with the message directly. Make this queue thread-safe, i.e. protect it with a critical section so that adding or removing from the queue is safe even when attempted simultaneously from different threads.
To request a new event handling, simply add the data to your queue. Only post a message to the receiving thread when the first data element is added to a previously empty queue. The receiving thread should then receive and process the message, and continue to pop data elements from the queue and call the matching event handlers until the queue is empty again. For best performance the queue should be locked as shortly as possible, and it should definitely be unlocked again temporarily while the event handler is called.
You should use PostMessage (asynch) or SendMessage (synch) API to send a message to' a window. You could use also some kind of "queue" or use the fantastic OmniThreadLibrary to' do this (highly recomended)
Declare a private member
FRecievedMessage: TMyMEssage
And a protected procedure
procedure PostRecievedMessage;
begin
if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, FRecievedMessage);
FRecievedMessage := nil;
end;
And change the code in the loop to
if (msgReceived) then begin
//message was received and now is contained in Msg variable
//fire OnMsgRcvdEvent and pass it the received message as parameter
FRecievedMessage := Msg;
Synchronize(PostRecievedMessage);
end;
If you want to do it completely asynch use PostMessage API instead.
Check docs for Synchronize method. It's designed for tasks like yours.
My framework does can do this for you if you wish to check it out (http://www.csinnovations.com/framework_overview.htm).

Thread context on Delphi

I am creating a server that starts a thread (listening thread). This thread listens for connections and does it's thing. Eventually it may receive some data, that I want to pass to the creator of the server:
My program --> Creates my server --> Creates the listener --> Creates a thread for every client
Now the question is: if I assign a property OnData = procedure (const Received: string) of object; to the server when I create it, and then, this same procedure is passed to the client threads, so when the thread get some data, it will be able to pass it back to the creator of the server. In pseudocode:
//My program
MyServer:= TServer.Create();
MyServer.Ondata:= SomeProcedure;
//The client thread
//When data is received
if (FServer <> nil) then
if Assigned(FServer.Ondata) then
Fserver.Ondata(ReceivedString)..
The questions here are:
1- I understand that if 10 threads execute this procedure at the same time, the procedure will be execute in the client thread context, so is the responsibility of my program (the actual "owner" of the procedure) to synchronize. Is that right?
2- If 10 different threads execute the procedure do I have 10 "instances" of the procedure with it's own data running at the same time?
3- Do I need to protect the OnData on the Server object as well (say, with a critical section), or it's OK to leave it to the "actual" procedure?
Yes. When the thread is running, all the code it runs gets executed within its own thread context, unless that code's been sent to another thread, for example with the Synchronize method.
Almost. You have 10 instances of the TThread object, each with its own data, running at the same time. There's only one copy of the actual code to the procedure, but multiple data objects can use it at once.
Any code that's not thread-safe (that might access the VCL, or that will write to any shared data, or read from shared data that something else might write to) needs to be protected.

Resources