Browse Source

Fix System.InvalidOperationException Dispatcher job loop detected (#20310)

pull/20322/head
Wiesław Šoltés 2 months ago
committed by GitHub
parent
commit
2bd109c808
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs
  2. 31
      tests/Avalonia.Headless.UnitTests/InputTests.cs

19
src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs

@ -142,20 +142,25 @@ public static class HeadlessWindowExtensions
action(GetImpl(topLevel)); action(GetImpl(topLevel));
RunJobsAndRender(); RunJobsAndRender();
void RunJobsAndRender() static void RunJobsAndRender()
{ {
var count = 0;
var dispatcher = Dispatcher.UIThread; var dispatcher = Dispatcher.UIThread;
while (dispatcher.HasJobsWithPriority(DispatcherPriority.MinimumActiveValue)) // Run jobs and render frames until everything is stable.
// We use a simple approach: run jobs, render, and repeat until
// there are no more pending jobs. The render timer tick can schedule
// new jobs, so we loop until stable.
for (var i = 0; i < 10; i++)
{ {
if (count >= 10)
throw new InvalidOperationException("Dispatcher job loop detected");
dispatcher.RunJobs(); dispatcher.RunJobs();
AvaloniaHeadlessPlatform.ForceRenderTimerTick(); AvaloniaHeadlessPlatform.ForceRenderTimerTick();
++count;
if (!dispatcher.HasJobsWithPriority(DispatcherPriority.MinimumActiveValue))
return;
} }
// Final attempt: run remaining jobs without rendering
dispatcher.RunJobs();
} }
} }

31
tests/Avalonia.Headless.UnitTests/InputTests.cs

@ -72,6 +72,37 @@ public class InputTests
Assert.True(_window.Position == newWindowPosition); Assert.True(_window.Position == newWindowPosition);
} }
#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
[AvaloniaFact]
#endif
public void Should_Click_Button_After_Explicit_RunJobs()
{
// Regression test for https://github.com/AvaloniaUI/Avalonia/issues/20309
// Ensure that calling Dispatcher.UIThread.RunJobs() before MouseDown does not throw
var button = new Button { Content = "Test content" };
_window.Content = button;
_window.Show();
Dispatcher.UIThread.RunJobs();
var clickCount = 0;
button.Click += (_, _) => clickCount++;
var point = new Point(button.Bounds.Width / 2, button.Bounds.Height / 2);
var translatePoint = button.TranslatePoint(point, _window);
// Move
_window.MouseMove(translatePoint!.Value, RawInputModifiers.None);
// Click
_window.MouseDown(translatePoint.Value, MouseButton.Left, RawInputModifiers.None);
_window.MouseUp(translatePoint.Value, MouseButton.Left, RawInputModifiers.None);
Assert.True(clickCount == 1);
}
#if NUNIT #if NUNIT
[TearDown] [TearDown]
public void TearDown() public void TearDown()

Loading…
Cancel
Save