Skip to content
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 @@ -58,7 +58,7 @@ List<Task> GenerateTasks()

#if UITEST
[Test]
[Microsoft.Maui.Controls.Compatibility.UITests.FailsOnMauiIOS]
[Microsoft.Maui.Controls.Compatibility.UITests.MovedToAppium]
public void ShouldNotCrash()
{
RunningApp.Tap(q => q.Marked("crashButton"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ public object GetService(Type serviceType)
if (serviceType == typeof(IDispatcher))
return _dispatcher;

if (serviceType == typeof(ApplicationDispatcher))
return new ApplicationDispatcher(_dispatcher);

throw new NotImplementedException();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.None, 1, "Application.Current.Dispatcher.IsDispatchRequired race condition causes crash")]
public class IsInvokeRequiredRaceCondition : TestContentPage
{
protected override void Init()
{
var button = new Button
{
AutomationId = "crashButton",
Text = "Start Test"
};

var success = new Label { Text = "Success", IsVisible = false, AutomationId = "successLabel" };

var instructions = new Label { Text = "Click the Start Test button. " };

Content = new StackLayout
{
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
Children = { instructions, success, button }
};

button.Clicked += async (sender, args) =>
{
await Task.WhenAll(GenerateTasks());
success.IsVisible = true;
};
}

List<Task> GenerateTasks()
{
var result = new List<Task>();

for (int n = 0; n < 1000; n++)
{
result.Add(Task.Run(() => { var t = Application.Current.Dispatcher.IsDispatchRequired; }));
}

return result;
}

}
15 changes: 11 additions & 4 deletions src/Controls/src/Core/DispatcherExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Hosting;

namespace Microsoft.Maui.Controls
{
Expand All @@ -22,11 +23,17 @@ bindableObject is Element element &&
if (Dispatcher.GetForCurrentThread() is IDispatcher globalDispatcher)
return globalDispatcher;

// If BO is of type Application then check for its Dispatcher
// If BO is of type Application then return the Dispatcher from ApplicationDispatcher
if (bindableObject is Application app &&
app.FindMauiContext() is IMauiContext appMauiContext &&
appMauiContext.Services.GetService<IDispatcher>() is IDispatcher appHandlerDispatcher)
return appHandlerDispatcher;
app.FindMauiContext() is IMauiContext appMauiContext)
{
if (appMauiContext.Services.GetOptionalApplicationDispatcher() is IDispatcher appDispatcherServiceDispatcher)
return appDispatcherServiceDispatcher;

// If BO is of type Application then check for its Dispatcher
if (appMauiContext.Services.GetService<IDispatcher>() is IDispatcher appHandlerDispatcher)
return appHandlerDispatcher;
}

// try looking on the static app
// We don't include Application because Application.Dispatcher will call
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.AppiumTests.Issues;

public class IsInvokeRequiredRaceCondition : _IssuesUITest
{
public IsInvokeRequiredRaceCondition(TestDevice device) : base(device)
{
}

public override string Issue => "Application.Current.Dispatcher.IsDispatchRequired race condition causes crash";

[Test]
public void ApplicationDispatcherIsInvokeRequiredRaceConditionCausesCrash()
{
App.WaitForElement("crashButton");
App.Click("crashButton");
App.WaitForElement("successLabel");
}
}
4 changes: 2 additions & 2 deletions src/Core/src/Hosting/Dispatching/AppHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ static IDispatcher GetDispatcher(IServiceProvider services)
services.CreateLogger<Dispatcher>()?.LogWarning("Replaced an existing DispatcherProvider with one from the service provider.");
}

return Dispatcher.GetForCurrentThread()!;
return Dispatcher.GetForCurrentThread() ?? services.GetRequiredService<ApplicationDispatcher>().Dispatcher;
Comment thread
PureWeen marked this conversation as resolved.
}

class ApplicationDispatcherInitializer : IMauiInitializeService
Expand All @@ -84,4 +84,4 @@ public void Initialize(IServiceProvider services)
}
}
}
}
}
3 changes: 3 additions & 0 deletions src/Core/src/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.Tizen")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.ControlGallery")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.ControlGallery.Android")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Android.UITests")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.iOS.UITests")]
[assembly: InternalsVisibleTo("WinUI.UITests")]
Comment thread
PureWeen marked this conversation as resolved.
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.ControlGallery.Tizen")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Xaml")]
[assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Xaml.UnitTests")]
Expand Down