softtouch 9 Posted June 6, 2022 I need to call a function, with returns a string. Inside this function is a TCP request sent and the function wait for the reply, and then return that reply. Unfortunately, I need to wait in this function until the reply is available, then return the result to the calling code. Example (not actual code): This calls a function "GetTCPIPC". ret:=GetTCPIPC('test only'); This is the function "GetTCPIPC": function GetTCPIPC(msg:string):string; begin if not clientclass.connected then begin result:=''; exit; end; clientclass.msgvalid:=false; idTCPClient.IOHandler.WriteLn(msg); while not clientclass.msgvalid do begin sleep(0); Application.ProcessMessages end; result:=clientclass.msgfromserver; end; This function must somehow wait until the flag msgvalid is set, and then return something to the caller. So with "Application.ProcessMessages", it works as it should, but I want to get rid of that ProcessMessages call, and still, this function need to return the string after it waits. How could I possible realize that? Share this post Link to post
ioan 45 Posted June 6, 2022 (edited) You could use a thread, something like this: type TMyThread = class(TThread) private FRet: string; protected procedure Execute; override; published property Ret: string read FRet; end; TForm5 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } procedure HandleThreadTerminate(Sender: TObject); end; var Form5: TForm5; implementation {$R *.dfm} procedure TForm5.Button1Click(Sender: TObject); var Thread: TMyThread; begin Thread := TMyThread.Create(true); Thread.FreeOnTerminate := true; Thread.OnTerminate := HandleThreadTerminate; Thread.Start; end; procedure TForm5.HandleThreadTerminate(Sender: TObject); begin caption := TMyThread(Sender).Ret; end; { TMyThread } procedure TMyThread.Execute; begin if not clientclass.connected then begin FRet :=''; exit; end; clientclass.msgvalid:=false; idTCPClient.IOHandler.WriteLn(msg); while (not Terminated) and (not clientclass.msgvalid) do begin FRet:=clientclass.msgfromserver; TThread.Sleep(0); end; end; Edited June 6, 2022 by ioan Share this post Link to post
Remy Lebeau 1399 Posted June 6, 2022 38 minutes ago, softtouch said: This is the function "GetTCPIPC": ... This function must somehow wait until the flag msgvalid is set, and then return something to the caller. So with "Application.ProcessMessages", it works as it should, but I want to get rid of that ProcessMessages call, and still, this function need to return the string after it waits. How could I possible realize that? It seems like you are receiving the server's response asynchronously. In which case, I would suggest not waiting for the response at all. Send the request, and then move on. Let the asynchronous handler notify your code whenever the response actually arrives. You can always disable the UI in the meantime, if needed. Otherwise, if you must make the function act synchronously, even though the response is asynchronous, then at least consider waiting on a TEvent instead of a boolean, eg: function GetTCPIPC(const msg: string): string; begin if not clientclass.Connected then begin Result := ''; Exit; end; clientclass.MsgEvent.Reset; idTCPClient.IOHandler.WriteLn(msg); while clientclass.MsgEvent.WaitFor(10) <> wrSignaled do Application.ProcessMessages; Result := clientclass.msgfromserver; end; Otherwise, consider changing the function to read the response directly in the function itself, not asynchronously from elsewhere. That way, you can place a TIdAntiFreeze component on your Form to keep the UI responsive while the TCP socket is blocking the UI thread, eg: function GetTCPIPC(const msg: string): string; begin if not clientclass.Connected then begin Result := ''; Exit; end; IdTCPClient.IOHandler.WriteLn(msg); //read response here Result := msgfromserver; end; Though, you really should not be doing socket I/O in the UI thread at all. Consider a threaded approach, similar to what ioan showed earlier. 1 Share this post Link to post
softtouch 9 Posted June 7, 2022 Thank you ioan and Remy! The problem is, the main code should not continue until the result is received, thats crucial. Its has to behave just like a call to normal function which return a result. It will also be called from many units. Share this post Link to post
FPiette 384 Posted June 7, 2022 You could use a non-blocking asynchronous TCP component such as ICS (TWSocket and other for high level protocols). With ICS, methods like Connect, Send, Receive and other are not blocking, they are merely a request which are almost instantaneous. Later when connection is established, data received or sent, you have an event. The UI is never blocked, even with hundreds of active connections. No need to use thread of wait loop (Of course you may use both if you like complexity). You can install ICS from the IDE using GetIT, or download a zip file, or use the SVN repository. Have a look at http://wiki.overbyte.eu/wiki/index.php/ICS_Download. Dephi-Praxis has an ICS dedicated support forum : https://en.delphipraxis.net/forum/37-ics-internet-component-suite/ 1 Share this post Link to post
Fr0sT.Brutal 900 Posted June 7, 2022 1 hour ago, softtouch said: The problem is, the main code should not continue until the result is received, thats crucial. Its has to behave just like a call to normal function which return a result. It will also be called from many units. You either block or not block. No 3rd option until async/await is added to Delphi. So you can implement your call as fully sync and place it to a thread when keeping interaction is needed or make it async so that you start it and go further and callback will be called when the request is done. Share this post Link to post
Uwe Raabe 2058 Posted June 7, 2022 1 hour ago, softtouch said: The problem is, the main code should not continue until the result is received, thats crucial. That exactly describes a blocking behavior. As long as the specification says blocking and you want non-blocking, you better change the specification first before doing anything with the code. 2 Share this post Link to post
dummzeuch 1505 Posted June 7, 2022 1 hour ago, Uwe Raabe said: That exactly describes a blocking behavior. As long as the specification says blocking and you want non-blocking, you better change the specification first before doing anything with the code. The problem here is, that in Delphi "blocking" means that the UI freezes, unless he calls Application.ProcessMessages, which has its own drawbacks (e.g. other events can be fired, some of which may not be desired). 1 Share this post Link to post
Remy Lebeau 1399 Posted June 7, 2022 11 hours ago, softtouch said: The problem is, the main code should not continue until the result is received, thats crucial. Its has to behave just like a call to normal function which return a result. It will also be called from many units. I've already said my piece about that. Share this post Link to post