|
@@ -1,6 +1,8 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
+using System.Threading;
|
|
|
using Avalonia.Threading;
|
|
|
using Xunit;
|
|
|
namespace Avalonia.Base.UnitTests;
|
|
@@ -9,9 +11,14 @@ public class DispatcherTests
|
|
|
{
|
|
|
class SimpleDispatcherImpl : IDispatcherImpl, IDispatcherImplWithPendingInput
|
|
|
{
|
|
|
- public bool CurrentThreadIsLoopThread => true;
|
|
|
-
|
|
|
- public void Signal() => AskedForSignal = true;
|
|
|
+ private Thread _loopThread = Thread.CurrentThread;
|
|
|
+ private object _lock = new();
|
|
|
+ public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _loopThread;
|
|
|
+ public void Signal()
|
|
|
+ {
|
|
|
+ lock (_lock)
|
|
|
+ AskedForSignal = true;
|
|
|
+ }
|
|
|
|
|
|
public event Action Signaled;
|
|
|
public event Action Timer;
|
|
@@ -27,9 +34,12 @@ public class DispatcherTests
|
|
|
|
|
|
public void ExecuteSignal()
|
|
|
{
|
|
|
- if (!AskedForSignal)
|
|
|
- return;
|
|
|
- AskedForSignal = false;
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ if (!AskedForSignal)
|
|
|
+ return;
|
|
|
+ AskedForSignal = false;
|
|
|
+ }
|
|
|
Signaled?.Invoke();
|
|
|
}
|
|
|
|
|
@@ -45,6 +55,61 @@ public class DispatcherTests
|
|
|
public bool HasPendingInput => TestInputPending == true;
|
|
|
public bool? TestInputPending { get; set; }
|
|
|
}
|
|
|
+
|
|
|
+ class SimpleDispatcherWithBackgroundProcessingImpl : SimpleDispatcherImpl, IDispatcherImplWithExplicitBackgroundProcessing
|
|
|
+ {
|
|
|
+ public bool AskedForBackgroundProcessing { get; private set; }
|
|
|
+ public event Action ReadyForBackgroundProcessing;
|
|
|
+ public void RequestBackgroundProcessing()
|
|
|
+ {
|
|
|
+ if (!CurrentThreadIsLoopThread)
|
|
|
+ throw new InvalidOperationException();
|
|
|
+ AskedForBackgroundProcessing = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void FireBackgroundProcessing()
|
|
|
+ {
|
|
|
+ if(!AskedForBackgroundProcessing)
|
|
|
+ return;
|
|
|
+ AskedForBackgroundProcessing = false;
|
|
|
+ ReadyForBackgroundProcessing?.Invoke();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ class SimpleControlledDispatcherImpl : SimpleDispatcherWithBackgroundProcessingImpl, IControlledDispatcherImpl
|
|
|
+ {
|
|
|
+ private readonly bool _useTestTimeout = true;
|
|
|
+ private readonly CancellationToken? _cancel;
|
|
|
+ public int RunLoopCount { get; private set; }
|
|
|
+
|
|
|
+ public SimpleControlledDispatcherImpl()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public SimpleControlledDispatcherImpl(CancellationToken cancel, bool useTestTimeout = false)
|
|
|
+ {
|
|
|
+ _useTestTimeout = useTestTimeout;
|
|
|
+ _cancel = cancel;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void RunLoop(CancellationToken token)
|
|
|
+ {
|
|
|
+ RunLoopCount++;
|
|
|
+ var st = Stopwatch.StartNew();
|
|
|
+ while (!token.IsCancellationRequested || _cancel?.IsCancellationRequested == true)
|
|
|
+ {
|
|
|
+ FireBackgroundProcessing();
|
|
|
+ ExecuteSignal();
|
|
|
+ if (_useTestTimeout)
|
|
|
+ Assert.True(st.ElapsedMilliseconds < 4000, "RunLoop exceeded test time quota");
|
|
|
+ else
|
|
|
+ Thread.Sleep(10);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
|
|
|
[Fact]
|
|
@@ -169,5 +234,24 @@ public class DispatcherTests
|
|
|
impl.TestInputPending = false;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ [Theory,
|
|
|
+ InlineData(false, false),
|
|
|
+ InlineData(false, true),
|
|
|
+ InlineData(true, false),
|
|
|
+ InlineData(true, true)]
|
|
|
+ public void CanWaitForDispatcherOperationFromTheSameThread(bool controlled, bool foreground)
|
|
|
+ {
|
|
|
+ var impl = controlled ? new SimpleControlledDispatcherImpl() : new SimpleDispatcherImpl();
|
|
|
+ var disp = new Dispatcher(impl);
|
|
|
+ bool finished = false;
|
|
|
+
|
|
|
+ disp.InvokeAsync(() => finished = true,
|
|
|
+ foreground ? DispatcherPriority.Default : DispatcherPriority.Background).Wait();
|
|
|
+
|
|
|
+ Assert.True(finished);
|
|
|
+ if (controlled)
|
|
|
+ Assert.Equal(foreground ? 0 : 1, ((SimpleControlledDispatcherImpl)impl).RunLoopCount);
|
|
|
+ }
|
|
|
+
|
|
|
}
|