Recent

Author Topic: How to synchronize main thread when FormDestroy?  (Read 4735 times)

sainimu78

  • Full Member
  • ***
  • Posts: 117
How to synchronize main thread when FormDestroy?
« on: December 05, 2016, 01:43:05 pm »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.OnIdTCPServerDisconnect;
  2. begin
  3.   Synchronize(@DeleteTheClientFromCheckListBox);
  4. end;

OnIdTCPServerDisconnect works fine when a client disconnected during the server.exe runtime, but Synchronize(@DeleteFromCheckListBox) will block when the server.exe close.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormDestroy;
  2. begin
  3.   idTcpSvr.Free;//idTcpSvr.Free will trigger OnIdTCPServerDisconnect event
  4. end;

The bad thing is not just blocking at idTcpSvr.Free, it is that I can not stop the idTcpSvr during runtime. it force me to do

Code: Pascal  [Select][+][-]
  1. type
  2.   TStopThread = class(TThread)
  3.   public
  4.     par:TfrmDFDevList;
  5.     procedure Execute; override;
  6.   end;
  7.  
  8. procedure TStopThread.Execute;
  9. begin
  10.   par.svrDFDev.Active := False;
  11.   Terminate;
  12. end;
  13.  
  14. procedure TForm1.btnStopClick(Sender:TObject);
  15. var
  16.   state:Longint;
  17. begin
  18.   cbAutoStart.Checked := False;
  19.   state := Ord(DF_ST_SUCCESS);
  20.   with TStopThread.Create(True) do
  21.   begin
  22.     par := Self;
  23.     FreeOnTerminate := True;
  24.     Start;
  25.     while(Terminated = False)do
  26.     begin
  27.       Application.ProcessMessages;
  28.       Sleep(10);
  29.     end;
  30.   end;              
  31. end;

Only can this way handle the server stopping problem during runtime but can not when form closing.

An awkward it is. There must be some ways go gracefully. Please help.
« Last Edit: December 05, 2016, 02:13:31 pm by sainimu78 »

Fungus

  • Sr. Member
  • ****
  • Posts: 353
Re: How to synchronize main thread when FormDestroy?
« Reply #1 on: December 05, 2016, 02:23:01 pm »
You need to close the TcpServer before you reach FormDestroy - add a OnClose handler to the form (TForm.OnClose), close the TcpServer in that event and wait for threads to finish before you exit OnClose.

sainimu78

  • Full Member
  • ***
  • Posts: 117
Re: How to synchronize main thread when FormDestroy?
« Reply #2 on: December 05, 2016, 02:50:39 pm »
You need to close the TcpServer before you reach FormDestroy - add a OnClose handler to the form (TForm.OnClose), close the TcpServer in that event and wait for threads to finish before you exit OnClose.

TForm1 is a sub form and the OnClose will not be triggered when the main form close
« Last Edit: December 05, 2016, 04:10:39 pm by sainimu78 »

sainimu78

  • Full Member
  • ***
  • Posts: 117
Re: How to synchronize main thread when FormDestroy?
« Reply #3 on: December 06, 2016, 02:14:18 am »
This is a common problem for a new to lazarus, isn't it?

Fungus

  • Sr. Member
  • ****
  • Posts: 353
Re: How to synchronize main thread when FormDestroy?
« Reply #4 on: December 06, 2016, 12:54:15 pm »
You need to gracefully close the TcpServer in the forms onclose method. If your server is located in a sub form, you must relay the onclose from the parent form to the sub form. When you reach FormDestroy some of the unfinished threads may attempt to access controls or whatever which has already been released.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to synchronize main thread when FormDestroy?
« Reply #5 on: December 06, 2016, 09:00:51 pm »
OnIdTCPServerDisconnect works fine when a client disconnected during the server.exe runtime, but Synchronize(@DeleteFromCheckListBox) will block when the server.exe close.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormDestroy;
  2. begin
  3.   idTcpSvr.Free;//idTcpSvr.Free will trigger OnIdTCPServerDisconnect event
  4. end;

You can't use TThread.Synchronize() while the main thread is blocked deactivating the server.  Server deactivation is a blocking operation, so the main thread will be blocked from processing Synchronize() requests until after deactivation is finished.  But the deactivation is blocked waiting for the server threads to terminate, which is waiting on Synchronize() to finish.  Deadlock.

So, either:

1. use TThread.Queue() instead of TThread.Synchronize() so the server thread is not blocked during deactivation.

2. deactivate the server in a worker thread so the main thread is not blocked.

In the second case, you can simplify your shutdown code using TThread.WaitFor() instead of a manual message loop, as WaitFor() handles Synchronize() and Queue() requests while waiting:

Code: [Select]
with TStopThread.Create(True) do
try
  par := Self;
  Start;
  WaitFor;
finally
  Free;
end;               

Alternatively:

Code: [Select]
with TThread.CreateAnonymousThread(
  procedure
  begin
    svrDFDev.Active := False;
  end
) do
try
  FreeOnTerminate := False; // <-- CreateAnonymousThread() sets this to True by default
  Start;
  WaitFor;
finally
  Free;
end;
« Last Edit: December 07, 2016, 05:29:08 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

sainimu78

  • Full Member
  • ***
  • Posts: 117
Re: How to synchronize main thread when FormDestroy?
« Reply #6 on: December 07, 2016, 03:38:24 am »


Thank you, all works as you said.

Could you tell me where you learn this?

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to synchronize main thread when FormDestroy?
« Reply #7 on: December 07, 2016, 05:31:01 am »
Could you tell me where you learn this?

Experience, and having an understanding of how Indy and the RTL behave by looking at their source codes.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

sainimu78

  • Full Member
  • ***
  • Posts: 117
Re: How to synchronize main thread when FormDestroy?
« Reply #8 on: December 07, 2016, 12:11:09 pm »


One word, Cool, thank you for sharing.

 

TinyPortal © 2005-2018