Recent

Author Topic: GUI application hangs (forever) if heavy thread starts in parallel  (Read 11350 times)

Dmitry24

  • New Member
  • *
  • Posts: 22
Unfortunately I encountered a strange problem when heavy thread is started programmatically at startup in parallel to main application. Application window sometimes stops responding at all (even button is not drawn). The only solution found is to start thread on timer and with the timer interval large enough. However it seems unreliable solution. The later thread starts the rarer the problem happen (but no guarantee it disappears at all for a certain interval). In most of examples threads are usually started on button clicks that is some time after form creation so that examples are not affected.

The problems never happens while debugging.
If the thread gives timeslices (e.g. threadswitch or even there is no heavy loop) there is no problem.

Below is the example application where several points were tried to start the thread. The only workaround for me is to start inside the timer with interval of 200 ms (20 ms is not enough) or to start manually with button or to use threadswitch inside the thread for time slices. Otherwise window may hangs (however not always).

Please give any advice...
Lazarus 1.6 64-bit compiled in DEBUG mode and run on Ubuntu 14

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Timer1: TTimer;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure Idle(Sender: TObject; var Done: boolean);
  21.     procedure Timer1Timer(Sender: TObject);
  22.   private
  23.     threadcreated: boolean;
  24.     { private declarations }
  25.   public
  26.     { public declarations }
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. const
  37.   appTerminate: boolean = false;
  38.  
  39. type
  40.     TThread1 = class(TThread)
  41.     protected
  42.       procedure Execute; override;
  43.     public
  44.       Constructor Create(CreateSuspended : boolean);
  45.     end;
  46.  
  47.   constructor TThread1.Create(CreateSuspended : boolean);
  48.   begin
  49.     FreeOnTerminate := True;
  50.     inherited Create(CreateSuspended);
  51.   end;
  52.  
  53.   procedure TThread1.Execute;
  54.   var
  55.     i, j: integer;
  56.   begin
  57.     j:=0;
  58.     for i:=0 to 200000000 do begin
  59.       j:=j+1;
  60.       //if (j and $FFFF=0) then threadswitch;
  61.     end;
  62.     appTerminate:=true;
  63.   end;
  64.  
  65. { TForm1 }
  66.  
  67. procedure TForm1.Button1Click(Sender: TObject);
  68. begin
  69.   //TThread1.Create(false);
  70. end;
  71.  
  72. procedure TForm1.Idle(Sender: TObject; var Done: boolean);
  73. begin
  74.   if (appTerminate) then begin
  75.     Application.Terminate;
  76.     exit;
  77.   end;
  78.  
  79.   Done:=true;
  80.  
  81.   if (threadcreated) then exit;
  82.   //threadcreated:=true; TThread1.Create(false);
  83. end;
  84.  
  85. procedure TForm1.Timer1Timer(Sender: TObject);
  86. begin
  87.   if (threadcreated) then exit;
  88.   //threadcreated:=true; TThread1.Create(false);
  89. end;
  90.  
  91. procedure TForm1.FormCreate(Sender: TObject);
  92. begin
  93.   threadcreated:=false;
  94.   Application.OnIdle:={$IFDEF FPC}@{$ENDIF}Idle;
  95.   threadcreated:=true; TThread1.Create(false);
  96. end;
  97.  
  98. end.
  99.  
  100.  
  101. program project1;
  102.  
  103. {$mode objfpc}{$H+}
  104.  
  105. uses
  106.   {$IFDEF UNIX}
  107.   cthreads, cmem,
  108.   {$ENDIF}
  109.   Interfaces, // this includes the LCL widgetset
  110.   Forms, Unit1
  111.   { you can add units after this };
  112.  
  113. {$R *.res}
  114.  
  115. begin
  116.   RequireDerivedFormResource:=True;
  117.   Application.Initialize;
  118.   Application.CreateForm(TForm1, Form1);
  119.   Application.Run;
  120. end.
  121.  
  122.  
  123.  

Finally, it is found that even the following app freezes sometimes when run not from IDE (for those who does not read the rest).

Code: Pascal  [Select][+][-]
  1.    
  2. program project1;
  3.      
  4.     {$mode objfpc}{$H+}
  5.      
  6.     uses
  7.       Interfaces, // this includes the LCL widgetset
  8.       Forms;
  9.      
  10.     {$R *.res}
  11.      
  12.     var
  13.       Form1: TForm;
  14.     begin
  15.       Application.Initialize;
  16.       Application.CreateForm(TForm, Form1);
  17.       Application.Run;
  18.     end.
  19.  
« Last Edit: July 05, 2016, 10:47:50 am by Dmitry24 »

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #1 on: July 04, 2016, 04:05:36 pm »
I can't reproduce this in virtualbox with Ubuntu 16 with the given source.

I do have a small note about the {$IFDEF FPC}@{$ENDIF} use.
If you use {$MODE DELPHI} this will not work correctly (@ shouldn't be used while FPC is still defined).

You could use
Code: Pascal  [Select][+][-]
  1. Application.OnIdle:={$IFNDEF FPC_DELPHI}@{$ENDIF}Idle;
  2.  
or if the source also needs to be compiled in Delphi:
Code: Pascal  [Select][+][-]
  1. Application.OnIdle:={$IFDEF FPC}{$IFNDEF FPC_DELPHI}@{$ENDIF}{$ENDIF}Idle;

Dmitry24

  • New Member
  • *
  • Posts: 22
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #2 on: July 04, 2016, 05:23:45 pm »
Thank you for responding. Thank you for the note. {$mode objfpc} directive is on the second line. Idle may be simply removed.

Sometimes app freezes on startup (empty not responding window appears, sometimes buttons is drawn but also not responding). The second thread always completes successfully. If the second thread interacts with the main thread it also freezes. Even with 200 ms delay it sometimes freezes, and almost always without a delay, in release mode too. The same on Ubuntu 16 in Vmware.

Additionally I found it freezes rarely when run from GUI file panel (Files) and almost always freezes when run from terminal. Additionally it seems never freezing when I run using sudo.

It seems there is no problem with 32-bit compiled app, only with 64-bit.
Could you sent the compiled app to make sure it is not a wrong compilation? How to attach a big file?
« Last Edit: July 04, 2016, 05:29:16 pm by Dmitry24 »

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #3 on: July 04, 2016, 06:15:57 pm »
Thank you for the note. {$mode objfpc} directive is on the second line. Idle may be simply removed.
In case you use objfpc you could have omitted the {$IFDEF FPC} completely. Normally this is done for using same source in Delphi. But if used in FPC you could have mode Delphi at the top in which case the IFDEFs I gave were more appropriate. (but indeed, for this test-case they are not necessary).

Quote
Additionally I found it freezes rarely when run from GUI file panel (Files) and almost always freezes when run from terminal. Additionally it seems never freezing when I run using sudo.
Yes, I have it now too. It always works in the IDE. But I have it just the other way around. It seems to ALWAYS work if I run from terminal. But sometimes freezes (after 3 or 4 tries) when run from the file-explorer  :D

You can't attach binaries here on the site (only small zip-files for sourcecode).
Attached is my test-project and a temporary dropbox-link to the project1 executable:
https://www.dropbox.com/s/4fhyrmujuki9qlc/project1.zip?dl=0

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #4 on: July 04, 2016, 06:26:04 pm »
Additionally the problem might be in the way you terminate the program.

Have you tried a loop of 800000000.
When I do that it always works. The program has sufficient time to complete the creation of the Form1. When you have a small loop the thread is ending really soon and if the form isn't drawn yet (because of the busy thread) you terminate the program too soon.

Does increasing the loop work for you?
If that doesn't work... what happens if you just comment out the Application.Terminate ?

Dmitry24

  • New Member
  • *
  • Posts: 22
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #5 on: July 04, 2016, 07:00:30 pm »
Historically I write for FPC mode objfpc and for Delphi so there are such defines.

Yes, I have it now too. It always works in the IDE.
Yes, I wrote the problems never happens while debugging. I mean in IDE it never happens.
Quote
But I have it just the other way around. It seems to ALWAYS work if I run from terminal. But sometimes freezes (after 3 or 4 tries) when run from the file-explorer  :D
Sometimes it works from terminal many times in a row for me.
Quote
You can't attach binaries here on the site (only small zip-files for sourcecode).
Thant you, it seems it is not necessary since the problem reproduced.
Quote
Additionally the problem might be in the way you terminate the program.
I tried without auto terminating at all (it was just for simplicity of testing). It freezes anyway. Example is below

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.     procedure FormCreate(Sender: TObject);
  19.   private
  20.     { private declarations }
  21.   public
  22.     { public declarations }
  23.   end;
  24.  
  25. var
  26.   Form1: TForm1;
  27.  
  28. implementation
  29.  
  30. {$R *.lfm}
  31.  
  32. type
  33.     TThread1 = class(TThread)
  34.     protected
  35.       procedure Execute; override;
  36.     public
  37.       Constructor Create(CreateSuspended : boolean);
  38.     end;
  39.  
  40.   constructor TThread1.Create(CreateSuspended : boolean);
  41.   begin
  42.     FreeOnTerminate := True;
  43.     inherited Create(CreateSuspended);
  44.   end;
  45.  
  46.   procedure TThread1.Execute;
  47.   var
  48.     i, j: integer;
  49.   begin
  50.     j:=0;
  51.     for i:=0 to 200000000 do begin
  52.       j:=j+1;
  53.       //if (j and $FFFF=0) then threadswitch; <- giving time slices helps
  54.     end;
  55.   end;
  56.  
  57. { TForm1 }
  58.  
  59. procedure TForm1.Button1Click(Sender: TObject);
  60. begin
  61.   TThread1.Create(false);
  62. end;
  63.  
  64. procedure TForm1.FormCreate(Sender: TObject);
  65. begin
  66.   TThread1.Create(false);
  67. end;
  68.  
  69. end.
  70.  

Increasing loop does not help me (just checked again). Are you sure it really always helps you? Main thread freezes during the loop in the second thread. I spent two days searching bug in my big multithreaded application... And finally reduced it to an example above :) I need to make some startup quite heavy calculations just after the form is created.
« Last Edit: July 04, 2016, 07:34:26 pm by Dmitry24 »

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #6 on: July 04, 2016, 08:43:08 pm »
Sometimes it works from terminal many times in a row for me.
Ok, finally after doing 25 times ./project1, enter, alt_f4, up, enter, alt_f4, up, enter, alt_f4 I finally had it hang on terminal too. But via the file-explorer it usually hangs the 1st through 4th time.

I've tried some things (dummy thread-initialization, CheckSynchronize()) but couldn't pin down why this would happen. But this goes beyond my ability to debug.

Even removing FreeOnTerminate and doing this makes it hang:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   t :TThread1;
  4. begin
  5.   t := TThread1.Create(false);
  6.   t.WaitFor;
  7.   t.Free;
  8. end;
In that case your thread is already ended when the form resumes creating.
It still hangs with a blank form.
(So I'm wondering if it's really your "busy" thread that's at fault or something else.

Even this makes it hang after enough times (so without running a thread):
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   t :TThread1;
  4. begin
  5.   //t := TThread1.Create(false);
  6.   //t.WaitFor;
  7.   //t.Free;
  8. end;
So it might be just a general problem which the thread makes worse.

Edit: Even starting a form with one button without the cthreads in the uses of the project, the project hangs after about 25 starts from the file-explorer.

Edit #2: Now even after 1 or 2 starts. Seems like 64bit Lazarus is really unstable.

Simple example of a button starting the same executable and exiting.
Press the button a few times and see it hang.
(How does it work for you?)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.     procedure FormCreate(Sender: TObject);
  19.   private
  20.     { private declarations }
  21.   public
  22.     { public declarations }
  23.   end;
  24.  
  25. var
  26.   Form1: TForm1;
  27.  
  28. implementation
  29. uses Process;
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.Button1Click(Sender: TObject);
  36. var
  37.   AProcess: TProcess;
  38. begin
  39.   AProcess := TProcess.Create(nil);
  40.   AProcess.Executable:= ParamStr(0);
  41.   AProcess.Execute;
  42.   AProcess.Free;
  43.   Close;
  44. end;
  45.  
  46. procedure TForm1.FormCreate(Sender: TObject);
  47. begin
  48. end;
  49.  
  50. end.
« Last Edit: July 04, 2016, 09:30:45 pm by rvk »

Dmitry24

  • New Member
  • *
  • Posts: 22
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #7 on: July 05, 2016, 07:54:59 am »
Yes, a few times with TProcess (more often from File Explorer). Finally, I removed everything and still was able to get freeze several times for the app:

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Interfaces, // this includes the LCL widgetset
  7.   Forms;
  8.  
  9. {$R *.res}
  10.  
  11. var
  12.   Form1: TForm;
  13. begin
  14.   Application.Initialize;
  15.   Application.CreateForm(TForm, Form1);
  16.   Application.Run;
  17. end.
  18.  

I don't know if all Uses are necessary for freeze because I had success (freeze) only two times for this app. Now I can't get freezing any more for such simple app regardless the number of retries. It seems some delay at startup may cause the freeze. A heavy thread or other circumstances may give such a delay.

Currently I continue testing and can't get freeze anymore for anyone of apps... So sometimes the problem may dissappear at all on a system and is not reproducible  >:(

After reloading the system the problem appears again even for the simplest app. I removed unnecessary Uses (updated the example) and still sometimes it freezes.
« Last Edit: July 05, 2016, 08:19:39 am by Dmitry24 »

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #8 on: July 05, 2016, 09:35:07 am »
Making a few changes in code, marked with comments (not testing, not sure if helps):
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Timer1: TTimer;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure Idle(Sender: TObject; var Done: boolean);
  21.     procedure Timer1Timer(Sender: TObject);
  22.   private
  23.     threadcreated: boolean;
  24.     { private declarations }
  25.   public
  26.     { public declarations }
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. const
  37.   appTerminate: boolean = false;
  38.  
  39. type
  40.     TThread1 = class(TThread)
  41.     protected
  42.       procedure Execute; override;
  43.     public
  44.       Constructor Create(CreateSuspended : boolean);
  45.     end;
  46.  
  47.   constructor TThread1.Create(CreateSuspended : boolean);
  48.   begin
  49.     inherited Create(CreateSuspended); // Normally inherit parent constructor first
  50.     FreeOnTerminate := True;
  51.     //inherited Create(CreateSuspended);
  52.   end;
  53.  
  54.   procedure TThread1.Execute;
  55.   var
  56.     i, j: integer;
  57.   begin
  58.     j:=0;
  59.     //for i:=0 to 200000000 do begin
  60.     for i:=0 to 200 do begin // Test with smaller value first, does it freeze?
  61.       j:=j+1;
  62.       //if (j and $FFFF=0) then threadswitch;
  63.     end;
  64.     appTerminate:=true;
  65.   end;
  66.  
  67. { TForm1 }
  68.  
  69. procedure TForm1.Button1Click(Sender: TObject);
  70. begin
  71.   //TThread1.Create(false);
  72. end;
  73.  
  74. procedure TForm1.Idle(Sender: TObject; var Done: boolean);
  75. begin
  76.   Done:=true; // Set first, otherwise it will remain false when it terminates
  77.   if (appTerminate) then begin
  78.     appTerminate:=false; // Just making sure it won't do this again, it's idle event afterall, who knows how often it's called
  79.     Application.Terminate;
  80.     exit;
  81.   end;
  82.  
  83.   //Done:=true;
  84.  
  85.   if (threadcreated) then exit;
  86.   //threadcreated:=true; TThread1.Create(false);
  87. end;
  88.  
  89. procedure TForm1.Timer1Timer(Sender: TObject);
  90. begin
  91.   if (threadcreated) then exit;
  92.   //threadcreated:=true; TThread1.Create(false);
  93. end;
  94.  
  95. procedure TForm1.FormCreate(Sender: TObject);
  96. begin
  97.   threadcreated:=false;
  98.   Application.OnIdle:={$IFDEF FPC}@{$ENDIF}Idle;
  99.   threadcreated:=true; TThread1.Create(false);
  100. end;
  101.  
  102. end.
  103.  
  104.  
  105. program project1;
  106.  
  107. {$mode objfpc}{$H+}
  108.  
  109. uses
  110.   {$IFDEF UNIX}
  111.   cthreads, cmem,
  112.   {$ENDIF}
  113.   Interfaces, // this includes the LCL widgetset
  114.   Forms, Unit1
  115.   { you can add units after this };
  116.  
  117. {$R *.res}
  118.  
  119. begin
  120.   RequireDerivedFormResource:=True;
  121.   Application.Initialize;
  122.   Application.CreateForm(TForm1, Form1);
  123.   Application.Run;
  124. end.

Dmitry24

  • New Member
  • *
  • Posts: 22
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #9 on: July 05, 2016, 10:41:49 am »
Please, read the previous message just before your answer... What changes could you suggest to the code above?

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #10 on: July 05, 2016, 03:01:14 pm »
I tried the original (with thread) on Mint and it worked fine (no hangs).
Reinstalled Ubuntu and Lazarus (with fpclazup) and again... after a few starts it hangs.

Not sure if that's because Ubuntu is way heavier than Mint, and consequently uses more resources which makes it hang, but either way... it shouldn't happen.

I wouldn't be comfortable with releasing a 64bit version of software which "might" hang unexpectedly.

Edit: yep. even an empty project with one button will hang if started repeatedly (sometimes after 4 or 5 tries, sometimes after much much more, like 50 tries but it eventually hangs).
« Last Edit: July 05, 2016, 03:04:10 pm by rvk »

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #11 on: July 05, 2016, 06:02:59 pm »
Can you guys check if Lazarus trunk and FPC trunk have same problem? Please disable any optimization options, too.

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #12 on: July 05, 2016, 09:41:35 pm »
O, I thought I tried trunk first but apparently that was for something else.

After some trouble with fpclazup I managed to install trunk.
First, with startlazarus, lazarus hanged a few times on the splash screen but finally I got it going.

But unfortunately, trunk had the same result. The simple TProcess/run_by_button example hangs after about 8 runs.

What did you mean by "disable any optimization options"?
In FPC/Lazarus during compile or in Ubuntu (VM)?

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #13 on: July 06, 2016, 02:30:45 am »
By disabling any optimization settings I mean compiling FPC, Lazarus and test program with "-O-"  option.

kapibara

  • Hero Member
  • *****
  • Posts: 610
Re: GUI application hangs (forever) if heavy thread starts in parallel
« Reply #14 on: July 06, 2016, 04:59:40 am »
FWIW, with no changes I compiled rvk's thread.zip project with Laz trunk and release fpc 3.0 under Ubuntu 16.04 - 64 bits. The application always starts and finishes, no matter if it starts from terminal, filemanager or Run from Lazarus.
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

 

TinyPortal © 2005-2018