Browse Source

Fixed some potential threading issues

pull/11214/head
Nikita Tsukanov 3 years ago
parent
commit
f0ea1f1116
  1. 57
      src/Avalonia.Base/Threading/DispatcherFrame.cs
  2. 6
      src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs

57
src/Avalonia.Base/Threading/DispatcherFrame.cs

@ -91,31 +91,44 @@ public class DispatcherFrame
internal void Run(IControlledDispatcherImpl impl)
{
// Since the actual platform run loop is controlled by a Cancellation token, we are restarting
// it if frame still needs to run
while (Continue)
RunCore(impl);
}
private void RunCore(IControlledDispatcherImpl impl)
{
if (_isRunning)
throw new InvalidOperationException("This frame is already running");
_isRunning = true;
try
{
_cancellationTokenSource = new CancellationTokenSource();
// Wake up the dispatcher in case it has pending jobs
Dispatcher.RequestProcessing();
impl.RunLoop(_cancellationTokenSource.Token);
}
finally
Dispatcher.VerifyAccess();
// Since the actual platform run loop is controlled by a Cancellation token, we have an
// outer loop that restarts the platform one in case Continue was set to true after being set to false
while (true)
{
_isRunning = false;
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = null;
// Take the instance lock since `Continue` is changed from one too
lock (Dispatcher.InstanceLock)
{
if (!Continue)
return;
if (_isRunning)
throw new InvalidOperationException("This frame is already running");
_cancellationTokenSource = new CancellationTokenSource();
_isRunning = true;
}
try
{
// Wake up the dispatcher in case it has pending jobs
Dispatcher.RequestProcessing();
impl.RunLoop(_cancellationTokenSource.Token);
}
finally
{
lock (Dispatcher.InstanceLock)
{
_isRunning = false;
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
}
}
internal void MaybeExitOnDispatcherRequest()
{

6
src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs

@ -58,6 +58,10 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
public void RunLoop(CancellationToken token)
{
CancellationTokenRegistration registration = default;
if (token.CanBeCanceled)
registration = token.Register(() => _wakeup.Set());
while (!token.IsCancellationRequested)
{
bool signaled;
@ -105,5 +109,7 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
else
_wakeup.WaitOne();
}
registration.Dispose();
}
}
Loading…
Cancel
Save