Julien Lebosquain 1 day ago
committed by GitHub
parent
commit
98aa77e7d0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 15
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  2. 9
      src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs
  3. 8
      src/Avalonia.Base/Threading/Dispatcher.cs
  4. 136
      src/Avalonia.Base/Threading/DispatcherTimer.cs

15
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@ -1,12 +1,11 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading;
using Avalonia.Utilities;
namespace Avalonia.Threading
{
/// <summary>
/// SynchronizationContext to be used on main thread
/// A <see cref="SynchronizationContext"/> that uses a <see cref="Dispatcher"/> to post messages.
/// </summary>
public class AvaloniaSynchronizationContext : SynchronizationContext
{
@ -16,7 +15,7 @@ namespace Avalonia.Threading
private readonly Dispatcher _dispatcher;
// This constructor is here to enforce STA behavior for unit tests
internal AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority, bool isStaThread = false)
internal AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority, bool isStaThread)
{
_dispatcher = dispatcher;
Priority = priority;
@ -26,17 +25,17 @@ namespace Avalonia.Threading
}
public AvaloniaSynchronizationContext()
: this(Dispatcher.UIThread, DispatcherPriority.Default, Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
: this(Dispatcher.CurrentDispatcher, DispatcherPriority.Default)
{
}
public AvaloniaSynchronizationContext(DispatcherPriority priority)
: this(Dispatcher.UIThread, priority, false)
: this(Dispatcher.CurrentDispatcher, priority)
{
}
public AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority)
: this(dispatcher, priority, false)
: this(dispatcher, priority, dispatcher.IsSta)
{
}
@ -55,7 +54,7 @@ namespace Avalonia.Threading
return;
}
SetSynchronizationContext(Dispatcher.UIThread.GetContextWithPriority(DispatcherPriority.Normal));
SetSynchronizationContext(Dispatcher.CurrentDispatcher.GetContextWithPriority(DispatcherPriority.Normal));
}
/// <inheritdoc/>
@ -105,7 +104,7 @@ namespace Avalonia.Threading
}
}
public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.UIThread, priority);
public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.CurrentDispatcher, priority);
public static RestoreContext Ensure(Dispatcher dispatcher, DispatcherPriority priority)
{
if (Current is AvaloniaSynchronizationContext avaloniaContext

9
src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs

@ -44,7 +44,14 @@ public partial class Dispatcher
return null;
}
}
/// <summary>
/// Gets the dispatcher for the UI thread.
/// </summary>
/// <remarks>
/// Control and libraries author are encouraged to use <see cref="CurrentDispatcher"/> and
/// <see cref="AvaloniaObject.Dispatcher"/> instead.
/// </remarks>
public static Dispatcher UIThread
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]

8
src/Avalonia.Base/Threading/Dispatcher.cs

@ -13,10 +13,6 @@ namespace Avalonia.Threading;
/// <summary>
/// Provides services for managing work items on a thread.
/// </summary>
/// <remarks>
/// In Avalonia, there is usually only a single <see cref="Dispatcher"/> in the application -
/// the one for the UI thread, retrieved via the <see cref="UIThread"/> property.
/// </remarks>
public partial class Dispatcher : IDispatcher
{
private IDispatcherImpl _impl;
@ -52,6 +48,8 @@ public partial class Dispatcher : IDispatcher
s_currentThreadDispatcher = new() { Reference = new WeakReference<Dispatcher>(this) });
}
IsSta = _thread.GetApartmentState() == ApartmentState.STA;
if (impl is null)
{
var st = Stopwatch.StartNew();
@ -70,6 +68,8 @@ public partial class Dispatcher : IDispatcher
public bool SupportsRunLoops => _controlledImpl != null;
internal bool IsSta { get; }
/// <summary>
/// Checks that the current thread is the UI thread.
/// </summary>

136
src/Avalonia.Base/Threading/DispatcherTimer.cs

@ -7,65 +7,102 @@ namespace Avalonia.Threading;
/// A timer that is integrated into the Dispatcher queues, and will
/// be processed after a given amount of time at a specified priority.
/// </summary>
public partial class DispatcherTimer
public class DispatcherTimer
{
internal static int ActiveTimersCount { get; private set; }
/// <summary>
/// Creates a timer that uses theUI thread's Dispatcher2 to
/// process the timer event at background priority.
/// Creates a timer that uses <see cref="Avalonia.Threading.Dispatcher.CurrentDispatcher"/> to
/// process the timer event at background priority.
/// </summary>
public DispatcherTimer() : this(DispatcherPriority.Background)
public DispatcherTimer()
: this(TimeSpan.Zero, DispatcherPriority.Background, Dispatcher.CurrentDispatcher)
{
}
/// <summary>
/// Creates a timer that uses the UI thread's Dispatcher2 to
/// process the timer event at the specified priority.
/// Creates a timer that uses <see cref="Avalonia.Threading.Dispatcher.CurrentDispatcher"/> to
/// process the timer event at the specified priority.
/// </summary>
/// <param name="priority">
/// The priority to process the timer at.
/// </param>
public DispatcherTimer(DispatcherPriority priority) : this(Threading.Dispatcher.UIThread, priority,
TimeSpan.FromMilliseconds(0))
/// <param name="priority">The priority to process the timer at.</param>
public DispatcherTimer(DispatcherPriority priority)
: this(TimeSpan.Zero, priority, Dispatcher.CurrentDispatcher)
{
}
/// <summary>
/// Creates a timer that uses the specified Dispatcher2 to
/// process the timer event at the specified priority.
/// Creates a timer that uses the specified <see cref="Avalonia.Threading.Dispatcher"/> to
/// process the timer event at the specified priority.
/// </summary>
/// <param name="priority">
/// The priority to process the timer at.
/// </param>
/// <param name="dispatcher">
/// The dispatcher to use to process the timer.
/// </param>
internal DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher) : this(dispatcher, priority,
TimeSpan.FromMilliseconds(0))
/// <param name="priority">The priority to process the timer at.</param>
/// <param name="dispatcher">The dispatcher to use to process the timer.</param>
public DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher)
: this(TimeSpan.Zero, priority, dispatcher)
{
}
/// <summary>
/// Creates a timer that uses the UI thread's Dispatcher2 to
/// process the timer event at the specified priority after the specified timeout.
/// Creates a timer that uses the specified <see cref="Avalonia.Threading.Dispatcher"/> to
/// process the timer event at the specified priority after the specified timeout.
/// </summary>
/// <param name="interval">
/// The interval to tick the timer after.
/// </param>
/// <param name="priority">
/// The priority to process the timer at.
/// </param>
/// <param name="callback">
/// The callback to call when the timer ticks.
/// </param>
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback)
: this(Threading.Dispatcher.UIThread, priority, interval)
/// <param name="interval">The interval to tick the timer after.</param>
/// <param name="priority">The priority to process the timer at.</param>
/// <param name="dispatcher">The dispatcher to use to process the timer.</param>
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, Dispatcher dispatcher)
{
if (callback == null)
ArgumentNullException.ThrowIfNull(dispatcher);
DispatcherPriority.Validate(priority, "priority");
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("Specified priority is not valid.", nameof(priority));
}
var ms = interval.TotalMilliseconds;
if (ms < 0)
{
throw new ArgumentNullException(nameof(callback));
throw new ArgumentOutOfRangeException(nameof(interval),
"TimeSpan period must be greater than or equal to zero.");
}
if (ms > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(interval),
"TimeSpan period must be less than or equal to Int32.MaxValue.");
}
_dispatcher = dispatcher;
_priority = priority;
_interval = interval;
}
/// <summary>
/// Creates a timer that uses <see cref="Avalonia.Threading.Dispatcher.CurrentDispatcher"/> to
/// process the timer event at the specified priority after the specified timeout and with
/// the specified handler.
/// </summary>
/// <param name="interval">The interval to tick the timer after.</param>
/// <param name="priority">The priority to process the timer at.</param>
/// <param name="callback">The callback to call when the timer ticks.</param>
/// <remarks>This constructor immediately starts the timer.</remarks>
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback)
: this(interval, priority, Dispatcher.CurrentDispatcher, callback)
{
}
/// <summary>
/// Creates a timer that uses the specified <see cref="Avalonia.Threading.Dispatcher"/> to
/// process the timer event at the specified priority after the specified timeout and with
/// the specified handler.
/// </summary>
/// <param name="interval">The interval to tick the timer after.</param>
/// <param name="priority">The priority to process the timer at.</param>
/// <param name="dispatcher">The dispatcher to use to process the timer.</param>
/// <param name="callback">The callback to call when the timer ticks.</param>
/// <remarks>This constructor immediately starts the timer.</remarks>
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, Dispatcher dispatcher, EventHandler callback)
: this(interval, priority, dispatcher)
{
ArgumentNullException.ThrowIfNull(callback);
Tick += callback;
Start();
@ -252,33 +289,6 @@ public partial class DispatcherTimer
/// </summary>
public object? Tag { get; set; }
internal DispatcherTimer(Dispatcher dispatcher, DispatcherPriority priority, TimeSpan interval)
{
if (dispatcher == null)
{
throw new ArgumentNullException(nameof(dispatcher));
}
DispatcherPriority.Validate(priority, "priority");
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("Specified priority is not valid.", nameof(priority));
}
if (interval.TotalMilliseconds < 0)
throw new ArgumentOutOfRangeException(nameof(interval), "TimeSpan period must be greater than or equal to zero.");
if (interval.TotalMilliseconds > Int32.MaxValue)
throw new ArgumentOutOfRangeException(nameof(interval),
"TimeSpan period must be less than or equal to Int32.MaxValue.");
_dispatcher = dispatcher;
_priority = priority;
_interval = interval;
}
private void Restart()
{
lock (_instanceLock)

Loading…
Cancel
Save