From 4cca53fc5f3ae5f6c31b73c691bb3cf06e9bb381 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Wed, 26 Oct 2022 12:40:16 +1100 Subject: [PATCH 1/8] feat: Moving navigation to background thread --- src/Uno.Extensions.Navigation.UI/Navigator.cs | 1 + .../Navigators/PanelVisiblityNavigator.cs | 27 ++++++++++--------- .../NavigationRequest.cs | 4 ++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Uno.Extensions.Navigation.UI/Navigator.cs b/src/Uno.Extensions.Navigation.UI/Navigator.cs index a6f51a32bb..a81ef9e011 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigator.cs @@ -49,6 +49,7 @@ protected Navigator( { if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Starting Navigation - Navigator: {this.GetType().Name} Request: {request.Route}"); request = request with { Source = this }; + return await Task.Run(() => NavigateAsync(request)); } diff --git a/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs b/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs index d74c288d7b..4d13c908c5 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigators/PanelVisiblityNavigator.cs @@ -99,27 +99,28 @@ protected override async Task RegionCanNavigate(Route route, RouteInfo? ro return path; } - protected override Task PostNavigateAsync() + protected override async Task PostNavigateAsync() { if (Control is not null) { - foreach (var child in Control.Children.OfType()) + await Dispatcher.ExecuteAsync(async cancellation => { - if(child == CurrentlyVisibleControl) + foreach (var child in Control.Children.OfType()) { - child.Opacity = 1; - child.Visibility = Visibility.Visible; - } - else - { - child.Opacity = 0; - child.Visibility = Visibility.Collapsed; + if (child == CurrentlyVisibleControl) + { + child.Opacity = 1; + child.Visibility = Visibility.Visible; + } + else + { + child.Opacity = 0; + child.Visibility = Visibility.Collapsed; + } } - } + }); } - - return Task.CompletedTask; } private FrameworkElement? FindByPath(string? path) diff --git a/src/Uno.Extensions.Navigation/NavigationRequest.cs b/src/Uno.Extensions.Navigation/NavigationRequest.cs index 068de8814f..ace7967a9c 100644 --- a/src/Uno.Extensions.Navigation/NavigationRequest.cs +++ b/src/Uno.Extensions.Navigation/NavigationRequest.cs @@ -1,9 +1,11 @@ namespace Uno.Extensions.Navigation; #pragma warning disable SA1313 // Parameter names should begin with lower-case letter -public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null, INavigator? Source = null) +public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null) #pragma warning restore SA1313 // Parameter names should begin with lower-case letter { + internal INavigator? Source { get; init; } + public override string ToString() => $"Request [Sender: {Sender.GetType().Name}, Route:{Route}, Result: {Result?.Name ?? "N/A"}]"; internal virtual IResponseNavigator? GetResponseNavigator(IResponseNavigatorFactory responseFactory, INavigator navigator) => default; From 6de6477176e60630d722e5ad7958f523763719f3 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Wed, 26 Oct 2022 14:41:35 +1100 Subject: [PATCH 2/8] feat: Moving navigation to background thread --- src/Uno.Extensions.Core/IDispatcher.cs | 5 +++++ src/Uno.Extensions.Navigation.UI/Dispatcher.cs | 11 ++++++++++- src/Uno.Extensions.Navigation.UI/Navigator.cs | 8 +++++++- src/Uno.Extensions.Navigation/NavigationRequest.cs | 4 +--- testing/TestHarness/TestHarness.UITest/Constants.cs | 2 +- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Uno.Extensions.Core/IDispatcher.cs b/src/Uno.Extensions.Core/IDispatcher.cs index c174969c03..02f18592f2 100644 --- a/src/Uno.Extensions.Core/IDispatcher.cs +++ b/src/Uno.Extensions.Core/IDispatcher.cs @@ -10,4 +10,9 @@ public interface IDispatcher /// An cancellation token to cancel the async operation. /// A ValueTask to asynchronously get the result of the operation. ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation); + + /// + /// Gets a value that specifies whether the current execution context is on the UI thread. + /// + bool HasThreadAccess { get; } } diff --git a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs index ac29d551d1..bfd02450db 100644 --- a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs +++ b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs @@ -33,5 +33,14 @@ public Dispatcher(FrameworkElement element) /// public async ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation) - => await _dispatcher.ExecuteAsync(func, cancellation); + { + if (HasThreadAccess) + { + return await func(cancellation); + } + return await _dispatcher.ExecuteAsync(func, cancellation); + } + + /// + public bool HasThreadAccess => _dispatcher.HasThreadAccess; } diff --git a/src/Uno.Extensions.Navigation.UI/Navigator.cs b/src/Uno.Extensions.Navigation.UI/Navigator.cs index a81ef9e011..30a23bab57 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigator.cs @@ -45,11 +45,17 @@ protected Navigator( var regionUpdateId = RouteUpdater?.StartNavigation(Region) ?? Guid.Empty; try { + + if (Dispatcher.HasThreadAccess) + { + if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Navigation started on UI thread, so moving to background thread"); + return await Task.Run(() => NavigateAsync(request)); + } + if (request.Source is null) { if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Starting Navigation - Navigator: {this.GetType().Name} Request: {request.Route}"); request = request with { Source = this }; - return await Task.Run(() => NavigateAsync(request)); } diff --git a/src/Uno.Extensions.Navigation/NavigationRequest.cs b/src/Uno.Extensions.Navigation/NavigationRequest.cs index ace7967a9c..068de8814f 100644 --- a/src/Uno.Extensions.Navigation/NavigationRequest.cs +++ b/src/Uno.Extensions.Navigation/NavigationRequest.cs @@ -1,11 +1,9 @@ namespace Uno.Extensions.Navigation; #pragma warning disable SA1313 // Parameter names should begin with lower-case letter -public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null) +public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null, INavigator? Source = null) #pragma warning restore SA1313 // Parameter names should begin with lower-case letter { - internal INavigator? Source { get; init; } - public override string ToString() => $"Request [Sender: {Sender.GetType().Name}, Route:{Route}, Result: {Result?.Name ?? "N/A"}]"; internal virtual IResponseNavigator? GetResponseNavigator(IResponseNavigatorFactory responseFactory, INavigator navigator) => default; diff --git a/testing/TestHarness/TestHarness.UITest/Constants.cs b/testing/TestHarness/TestHarness.UITest/Constants.cs index db7bc9d198..2443c49665 100644 --- a/testing/TestHarness/TestHarness.UITest/Constants.cs +++ b/testing/TestHarness/TestHarness.UITest/Constants.cs @@ -4,7 +4,7 @@ namespace TestHarness.UITest; public class Constants { - public readonly static string WebAssemblyDefaultUri = "https://localhost:51571"; + public readonly static string WebAssemblyDefaultUri = "https://localhost:64052"; public readonly static string iOSAppName = "uno.platform.extensions.demo"; public readonly static string AndroidAppName = "uno.platform.extensions.demo"; public readonly static string iOSDeviceNameOrId = "iPad Pro (12.9-inch) (4th generation)"; From bad93af52dacefd3574b98ba296cbfd3e72dc764 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Thu, 27 Oct 2022 11:34:02 +1100 Subject: [PATCH 3/8] fix: Adding InProgress property on request to prevent re-entrance to thread switching --- src/Uno.Extensions.Navigation.UI/Navigator.cs | 4 +++- src/Uno.Extensions.Navigation/NavigationRequest.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Uno.Extensions.Navigation.UI/Navigator.cs b/src/Uno.Extensions.Navigation.UI/Navigator.cs index 30a23bab57..ba5dcceec1 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigator.cs @@ -46,8 +46,10 @@ protected Navigator( try { - if (Dispatcher.HasThreadAccess) + if (Dispatcher.HasThreadAccess && + !request.InProgress) { + request = request with { InProgress = true }; // Prevent multiple re-entrance where HasThreadAccess always returns true eg WASM if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformationMessage($"Navigation started on UI thread, so moving to background thread"); return await Task.Run(() => NavigateAsync(request)); } diff --git a/src/Uno.Extensions.Navigation/NavigationRequest.cs b/src/Uno.Extensions.Navigation/NavigationRequest.cs index 068de8814f..24f27a10c3 100644 --- a/src/Uno.Extensions.Navigation/NavigationRequest.cs +++ b/src/Uno.Extensions.Navigation/NavigationRequest.cs @@ -4,6 +4,8 @@ public record NavigationRequest(object Sender, Route Route, CancellationToken? Cancellation = default, Type? Result = null, INavigator? Source = null) #pragma warning restore SA1313 // Parameter names should begin with lower-case letter { + internal bool InProgress { get; init; } + public override string ToString() => $"Request [Sender: {Sender.GetType().Name}, Route:{Route}, Result: {Result?.Name ?? "N/A"}]"; internal virtual IResponseNavigator? GetResponseNavigator(IResponseNavigatorFactory responseFactory, INavigator navigator) => default; From bbc4da84ee71bac3b1bd8cda94664e6d0c5f644b Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Thu, 27 Oct 2022 14:22:52 +1100 Subject: [PATCH 4/8] fix: Making PlatformHelper a static class --- src/Uno.Extensions.Core/PlatformHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uno.Extensions.Core/PlatformHelper.cs b/src/Uno.Extensions.Core/PlatformHelper.cs index b5728c2f62..de5f1cb9c4 100644 --- a/src/Uno.Extensions.Core/PlatformHelper.cs +++ b/src/Uno.Extensions.Core/PlatformHelper.cs @@ -1,6 +1,6 @@ namespace Uno.Extensions; -public class PlatformHelper +public static class PlatformHelper { private static bool _isNetCore; private static bool _initialized; From 728a5e731be8531dece5d040e9c9f69fcd154ab0 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Thu, 27 Oct 2022 14:23:27 +1100 Subject: [PATCH 5/8] fix: Getting win target to build and updating slnf for testharness --- testing/TestHarness/Directory.Build.targets | 5 +++++ testing/TestHarness/TestHarness-ui.slnf | 3 ++- testing/TestHarness/TestHarness-winui.slnf | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/testing/TestHarness/Directory.Build.targets b/testing/TestHarness/Directory.Build.targets index 0712cae045..b6ff508d83 100644 --- a/testing/TestHarness/Directory.Build.targets +++ b/testing/TestHarness/Directory.Build.targets @@ -34,4 +34,9 @@ + + + diff --git a/testing/TestHarness/TestHarness-ui.slnf b/testing/TestHarness/TestHarness-ui.slnf index bd035a1ba6..1a38851a44 100644 --- a/testing/TestHarness/TestHarness-ui.slnf +++ b/testing/TestHarness/TestHarness-ui.slnf @@ -8,6 +8,7 @@ "..\\..\\src\\Uno.Extensions.Authentication.UI\\Uno.Extensions.Authentication.UI.csproj", "..\\..\\src\\Uno.Extensions.Authentication\\Uno.Extensions.Authentication.csproj", "..\\..\\src\\Uno.Extensions.Configuration\\Uno.Extensions.Configuration.csproj", + "..\\..\\src\\Uno.Extensions.Core.Generators\\Uno.Extensions.Core.Generators.csproj", "..\\..\\src\\Uno.Extensions.Core\\Uno.Extensions.Core.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.UWP.Skia.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.UWP.Wasm.csproj", @@ -15,8 +16,8 @@ "..\\..\\src\\Uno.Extensions.Hosting\\Uno.Extensions.Hosting.csproj", "..\\..\\src\\Uno.Extensions.Http.Refit\\Uno.Extensions.Http.Refit.csproj", "..\\..\\src\\Uno.Extensions.Http\\Uno.Extensions.Http.csproj", - "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Localization.UI\\Uno.Extensions.Localization.UI.csproj", + "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Logging.Serilog\\Uno.Extensions.Logging.Serilog.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.UWP.Skia.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.UWP.Wasm.csproj", diff --git a/testing/TestHarness/TestHarness-winui.slnf b/testing/TestHarness/TestHarness-winui.slnf index 1bc234818d..2b9df3f0a7 100644 --- a/testing/TestHarness/TestHarness-winui.slnf +++ b/testing/TestHarness/TestHarness-winui.slnf @@ -8,6 +8,7 @@ "..\\..\\src\\Uno.Extensions.Authentication.UI\\Uno.Extensions.Authentication.WinUI.csproj", "..\\..\\src\\Uno.Extensions.Authentication\\Uno.Extensions.Authentication.csproj", "..\\..\\src\\Uno.Extensions.Configuration\\Uno.Extensions.Configuration.csproj", + "..\\..\\src\\Uno.Extensions.Core.Generators\\Uno.Extensions.Core.Generators.csproj", "..\\..\\src\\Uno.Extensions.Core\\Uno.Extensions.Core.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.WinUI.Skia.csproj", "..\\..\\src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.WinUI.Wasm.csproj", @@ -15,8 +16,8 @@ "..\\..\\src\\Uno.Extensions.Hosting\\Uno.Extensions.Hosting.csproj", "..\\..\\src\\Uno.Extensions.Http.Refit\\Uno.Extensions.Http.Refit.csproj", "..\\..\\src\\Uno.Extensions.Http\\Uno.Extensions.Http.csproj", - "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Localization.UI\\Uno.Extensions.Localization.WinUI.csproj", + "..\\..\\src\\Uno.Extensions.Localization\\Uno.Extensions.Localization.csproj", "..\\..\\src\\Uno.Extensions.Logging.Serilog\\Uno.Extensions.Logging.Serilog.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.WinUI.Skia.csproj", "..\\..\\src\\Uno.Extensions.Logging\\Uno.Extensions.Logging.WinUI.Wasm.csproj", From ac79ffd568bbb2f2ddc5300e9b8b71c4370e888c Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Thu, 27 Oct 2022 14:39:57 +1100 Subject: [PATCH 6/8] fix: Correct dispatching issue on wasm --- src/Uno.Extensions.Navigation.UI/Dispatcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs index bfd02450db..2921681660 100644 --- a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs +++ b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs @@ -34,7 +34,8 @@ public Dispatcher(FrameworkElement element) /// public async ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation) { - if (HasThreadAccess) + if (!PlatformHelper.IsWebAssembly && // Assume for web that we always call executeasync on dispatcher + HasThreadAccess) { return await func(cancellation); } From dfe57bab23e4c99a9ca38cb1de131397c88f6764 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Fri, 28 Oct 2022 01:11:17 +1100 Subject: [PATCH 7/8] feat: Adding threading support detection --- src/Uno.Extensions.Core/PlatformHelper.cs | 9 +++++++++ src/Uno.Extensions.Navigation.UI/Dispatcher.cs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Uno.Extensions.Core/PlatformHelper.cs b/src/Uno.Extensions.Core/PlatformHelper.cs index de5f1cb9c4..8e4624d534 100644 --- a/src/Uno.Extensions.Core/PlatformHelper.cs +++ b/src/Uno.Extensions.Core/PlatformHelper.cs @@ -30,6 +30,15 @@ public static bool IsNetCore } } + /// + /// Determines if the current runtime supports threading + /// + public static bool IsThreadingEnabled + => !IsWebAssembly || IsWebAssemblyThreadingSupported; + + private static bool IsWebAssemblyThreadingSupported { get; } = Environment.GetEnvironmentVariable("UNO_BOOTSTRAP_MONO_RUNTIME_CONFIGURATION").StartsWith("threads", StringComparison.OrdinalIgnoreCase); + + /// /// Initialization is performed explicitly to avoid a mono/mono issue regarding .cctor and FullAOT /// see https://github.com/unoplatform/uno/issues/5395 diff --git a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs index 2921681660..b8f592eedb 100644 --- a/src/Uno.Extensions.Navigation.UI/Dispatcher.cs +++ b/src/Uno.Extensions.Navigation.UI/Dispatcher.cs @@ -34,7 +34,7 @@ public Dispatcher(FrameworkElement element) /// public async ValueTask ExecuteAsync(AsyncFunc func, CancellationToken cancellation) { - if (!PlatformHelper.IsWebAssembly && // Assume for web that we always call executeasync on dispatcher + if (PlatformHelper.IsThreadingEnabled && HasThreadAccess) { return await func(cancellation); From ecbbac8a6f2b415e608d95e4f2a27fede5c725de Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Fri, 28 Oct 2022 03:59:43 +1100 Subject: [PATCH 8/8] Update src/Uno.Extensions.Core/PlatformHelper.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Laban --- src/Uno.Extensions.Core/PlatformHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uno.Extensions.Core/PlatformHelper.cs b/src/Uno.Extensions.Core/PlatformHelper.cs index 8e4624d534..602d810909 100644 --- a/src/Uno.Extensions.Core/PlatformHelper.cs +++ b/src/Uno.Extensions.Core/PlatformHelper.cs @@ -34,7 +34,7 @@ public static bool IsNetCore /// Determines if the current runtime supports threading /// public static bool IsThreadingEnabled - => !IsWebAssembly || IsWebAssemblyThreadingSupported; + { get; } = !IsWebAssembly || IsWebAssemblyThreadingSupported; private static bool IsWebAssemblyThreadingSupported { get; } = Environment.GetEnvironmentVariable("UNO_BOOTSTRAP_MONO_RUNTIME_CONFIGURATION").StartsWith("threads", StringComparison.OrdinalIgnoreCase);