Recent

Author Topic: Showing TForm from different thread hides some elements from Main Form  (Read 473 times)

tescosw

  • Newbie
  • Posts: 2
Hello,
when I call 'ShowModal' on my TForm implementation (Acts like a messagebox) from a non UI thread, some GUI elements from the Main form of the application just disappear. The problem showed up when I switched to building in 64-bit (Mac/Cocoa). Has anyone experienced this behavior? I've had similar issue with other components, but calling 'Invalidate' usually helped (Not this time tough...)

Thanks for any help  :)

Dmitry24

  • New Member
  • *
  • Posts: 13
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #1 on: September 10, 2019, 10:28:48 am »
https://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial
Quote
The win32, the gtk and the carbon interfaces support multi-threading. This means, TThread, critical sections and Synchronize work. But they are not thread safe. This means only one thread at a time can access the LCL. And since the main thread should never wait for another thread, it means only the main thread is allowed to access the LCL, which means anything that has to do with TControl, Application and LCL widget handles.

I think the same is true for Cocoa and you should use synchornization (e.g., sending messages or using TThread.Synchronize) to call LCL functions from a different thread otherwise the app will be unstable.
« Last Edit: September 10, 2019, 10:32:22 am by Dmitry24 »

Thaddy

  • Hero Member
  • *****
  • Posts: 8681
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #2 on: September 10, 2019, 10:31:19 am »
You should *never* call UI elements from a different thread than the main thread without synchronize. (And - what I suspect - create elements in a different thread.)
You can do the processing in a thread and when it is finished update the user interface.
« Last Edit: September 10, 2019, 10:33:56 am by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.

jamie

  • Hero Member
  • *****
  • Posts: 1922
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #3 on: September 10, 2019, 03:50:57 pm »
In windows magical things happen,,, We somply POSTMESSAGE(handle, WM_SHOW.....
not SENDMESSAGE.


Remy Lebeau

  • Hero Member
  • *****
  • Posts: 654
    • Lebeau Software
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #4 on: September 10, 2019, 10:44:52 pm »
In windows magical things happen,,, We somply POSTMESSAGE(handle, WM_SHOW.....
not SENDMESSAGE.

Note that (at least in Delphi, probably the same in FreePascal), the TWinControl.Handle property is not thread-safe due to the possibility of window recreations, and as such should NOT be used outside of the main UI thread.  Really bad things can happen during a race condition where the main UI thread is in the process of recreating a TWinControl window and a worker thread accesses the control's Handle property at the same time, triggering a secondary window creation that could cause resource leaks and even kill the UI control altogether making it unable to receive UI messages anymore.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

tescosw

  • Newbie
  • Posts: 2
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #5 on: September 11, 2019, 08:38:34 am »
https://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial
Quote
The win32, the gtk and the carbon interfaces support multi-threading. This means, TThread, critical sections and Synchronize work. But they are not thread safe. This means only one thread at a time can access the LCL. And since the main thread should never wait for another thread, it means only the main thread is allowed to access the LCL, which means anything that has to do with TControl, Application and LCL widget handles.

I think the same is true for Cocoa and you should use synchornization (e.g., sending messages or using TThread.Synchronize) to call LCL functions from a different thread otherwise the app will be unstable.

Well I do use TThread.Synchronize  :-\

Here's the code snippet. Basically what happens is that when an exception in code that runs on another thread rises, it should show a MessageBox via ManipulateGUI method.

Code: Pascal  [Select]
  1. begin
  2.       ManipulateGUI(@ShowGUIError);
  3. end;  

Code: Pascal  [Select]
  1. procedure TCommand.ManipulateGUI(AMethod: TThreadMethod);
  2. begin
  3.   TThread.Synchronize(TThread.CurrentThread, AMethod);
  4. end;

Code: Pascal  [Select]
  1. procedure TCommand.ShowGUIError;
  2. begin
  3.   TMessageBoxForm.ShowError(GUIMainForm.rsGUIErrorTitle, FGUIError);
  4. end;

And as I said, on Carbon it worked perfectly fine :-/ Or any other platform.

Thaddy

  • Hero Member
  • *****
  • Posts: 8681
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #6 on: September 11, 2019, 09:02:26 am »
In windows magical things happen,,, We somply POSTMESSAGE(handle, WM_SHOW.....
not SENDMESSAGE.
Partially right, but:
Wrong again: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postthreadmessagea or W

Ratio: you will run into a lot of trouble if you ignore the OS.
« Last Edit: September 11, 2019, 09:04:53 am by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.

jamie

  • Hero Member
  • *****
  • Posts: 1922
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #7 on: September 11, 2019, 04:55:52 pm »
Thaddy, Please tell MS that my code must be a failed because I READ the control handle, Not write, just read and Post messages to main threads from secondary threads with no issue. Been working for  years.

 
 

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 654
    • Lebeau Software
Re: Showing TForm from different thread hides some elements from Main Form
« Reply #8 on: September 11, 2019, 08:31:10 pm »
Thaddy, Please tell MS that my code must be a failed because I READ the control handle, Not write, just read and Post messages to main threads from secondary threads with no issue. Been working for  years.

Consider yourself lucky then.

If a worker thread DIRECTLY reads a control's Handle property each time it wants to post a message, that is not safe to do across thread boundaries, it carries a dangerous race condition that you have simply been fortunate enough to not have triggered yet.

Each time a control's Handle property is read, it checks if the control's window exists, and if not then creates it.  If the control's window is in the process of being (re)created at the same time that the thread reads the Handle property, it is possible for 2 new HWNDs to get created, one by the UI thread and one by the worker thread.  One of those HWNDs will be leaked, and if the HWND that is created by the worker thread ends up being the one assigned to the control, then the control will stop responding, and will then later crash during future window recreations, or during app shutdown, when the UI thread tries to destroy a window it doesn't own and raises an exception because DestroyWindow() fails.

On the other hand, if you read the control's Handle property one time and then copy that HWND to the thread and use it for posting, then there is no race condition.  If the control's window is ever destroyed then the thread's HWND will simply become invalid and subsequent posts will fail with error 1400 (ERROR_INVALID_WINDOW_HANDLE) until you update the thread with a new HWND after the control's window has been recreated.
« Last Edit: September 11, 2019, 08:33:30 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)