macOS AppBuilder must be created in same thread as Main() or else exception is thrown
See original GitHub issueHello! FIrst off - love what you folks are making. I work on the PowerShell team and we are working on porting some of our WPF stuff over to Avalonia like the ever famous command Out-GridView
which first appeared over 10 years ago!
Here’s the problem, to port this over, we need to be able to use AppBuilder
outside of the “main thread” - aka where main is.
NOTE: This issue only happens on macOS because this issue is in the native part of the code where avalonia calls
isMainThread
instead of doing what Linux and Windows does which is rely on saving theThread.CurrentThread
at constructor time and calling that the “main thread”
If you take the sample MVVM project generated by the donet new template, and make the following tweak to the Main()
function, you can easily repro the issue:
public static void Main(string[] args)
{
Task.Run(() => BuildAvaloniaApp().Start(AppMain, args)).Wait();
}
All I’m doing is wrapping it in a Task.Run
… which gives me:
dotnet run
Unhandled Exception: System.AggregateException: One or more errors occurred. (Call from invalid thread) ---> System.InvalidOperationException: Call from invalid thread
at Avalonia.Threading.Dispatcher.VerifyAccess() in D:\a\1\s\src\Avalonia.Base\Threading\Dispatcher.cs:line 51
at Avalonia.Rendering.RenderLoop.Add(IRenderLoopTask i) in D:\a\1\s\src\Avalonia.Visuals\Rendering\RenderLoop.cs:line 66
at Avalonia.Controls.AppBuilderBase`1.Setup() in D:\a\1\s\src\Avalonia.Controls\AppBuilderBase.cs:line 302
at Avalonia.Controls.AppBuilderBase`1.Start(AppMainDelegate main, String[] args) in D:\a\1\s\src\Avalonia.Controls\AppBuilderBase.cs:line 153
at MyApp.Program.<>c__DisplayClass0_0.<Main>b__0() in /Users/tyler/Code/CSharp/MyApp/Program.cs:line 17
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at MyApp.Program.Main(String[] args) in /Users/tyler/Code/CSharp/MyApp/Program.cs:line 17
Call from invalid thread
because I didn’t run the builder from the thread that Main
is running in.
Perhaps the path forward is to do the same thing that Windows and Linux does?
GUI creation with PowerShell is a big deal - see sites like posh gui that have made tooling around making GUI’s for PowerShell. It’d be awesome to support all the OS’s we support. macOS is a big one.
Let me know if I can help supply any more details. I tried to make the repro simple enough to repro out of PowerShell but if you want a PowerShell version, you can try out @adamdriscoll’s ps-avalonia module (first you need PowerShell Core, then you can run Install-Module PSAvalonia
) which, today, only works on windows and linux because of this issue.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:6
- Comments:5 (4 by maintainers)
Top GitHub Comments
@yatli I think that’s what we’ll have to try
Cocoa has its own idea about the thread being the main one and that’s the first thread of the app. See https://developer.apple.com/documentation/foundation/nsthread/1412704-ismainthread?language=objc and https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html#//apple_ref/doc/uid/10000057i-CH1-SW1 You can’t utilize any UI-related types on other threads than the main one. You can try asking Apple to change that, but I doubt that they’ll listen.