Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/9.0.1xx] [Blazor Hybrid] Fire and forget BlazorWebView disposal by default #25430

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ protected override AWebView CreatePlatformView()

private const string AndroidFireAndForgetAsyncSwitch = "BlazorWebView.AndroidFireAndForgetAsync";

private static bool IsAndroidFireAndForgetAsyncEnabled =>
AppContext.TryGetSwitch(AndroidFireAndForgetAsyncSwitch, out var enabled) && enabled;

protected override void DisconnectHandler(AWebView platformView)
{
platformView.StopLoading();
Expand All @@ -77,19 +74,25 @@ protected override void DisconnectHandler(AWebView platformView)
.DisposeAsync()
.AsTask()!;

if (IsAndroidFireAndForgetAsyncEnabled)
{
// If the app is configured to fire-and-forget via an AppContext Switch, we'll do that.
disposalTask.FireAndForget();
}
else
// When determining whether to block on disposal, we respect the more specific AndroidFireAndForgetAsync switch
// if specified. If not, we fall back to the general UseBlockingDisposal switch, defaulting to false.
var shouldBlockOnDispose = AppContext.TryGetSwitch(AndroidFireAndForgetAsyncSwitch, out var enableFireAndForget)
? !enableFireAndForget
: IsBlockingDisposalEnabled;
Comment on lines +79 to +81
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic ensures that if the "BlazorWebView.AndroidFireAndForgetAsync" switch was specified previously, we will continue to use that value, regardless of whether the new AppContext switch is also specified.


if (shouldBlockOnDispose)
{
// Otherwise by default, we'll synchronously wait for the disposal to complete. This can cause
// a deadlock, but is the original behavior.
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget(_logger);
}

_webviewManager = null;
}
Expand Down
5 changes: 5 additions & 0 deletions src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Components.WebView.Maui
#endif
public partial class BlazorWebViewHandler
{
private const string UseBlockingDisposalSwitch = "BlazorWebView.UseBlockingDisposal";

private static bool IsBlockingDisposalEnabled =>
AppContext.TryGetSwitch(UseBlockingDisposalSwitch, out var enabled) && enabled;

/// <summary>
/// This field is part of MAUI infrastructure and is not intended for use by application code.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Maui;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Handlers;
using WebView2Control = Microsoft.UI.Xaml.Controls.WebView2;
Expand All @@ -30,13 +31,24 @@ protected override void DisconnectHandler(WebView2Control platformView)
{
if (_webviewManager != null)
{
// Dispose this component's contents and block on completion so that user-written disposal logic and
// Blazor disposal logic will complete.
_webviewManager?
// Start the disposal...
var disposalTask = _webviewManager?
.DisposeAsync()
.AsTask()
.GetAwaiter()
.GetResult();
.AsTask()!;

if (IsBlockingDisposalEnabled)
{
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike with the other BlazorWebViewHandler implementations, there wasn't already an ILogger available to pass to the FireAndForget call here. If others think it's worth it or necessary to obtain one from the service provider, I won't object to adding it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code is fine.

}

_webviewManager = null;
}
Expand Down
24 changes: 18 additions & 6 deletions src/BlazorWebView/src/Maui/iOS/BlazorWebViewHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Maui;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Handlers;
using UIKit;
Expand Down Expand Up @@ -127,13 +128,24 @@ protected override void DisconnectHandler(WKWebView platformView)

if (_webviewManager != null)
{
// Dispose this component's contents and block on completion so that user-written disposal logic and
// Blazor disposal logic will complete.
_webviewManager?
// Start the disposal...
var disposalTask = _webviewManager?
.DisposeAsync()
.AsTask()
.GetAwaiter()
.GetResult();
.AsTask()!;

if (IsBlockingDisposalEnabled)
{
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget(_logger);
}

_webviewManager = null;
}
Expand Down