Browse Source
* Introduce Dispatcher.CurrentDispatcher, FromThread, AvaloniaObject.Dispatcher, allow dispatcher usage before avalonia initialization, auto-create valid dispatchers for new threads. * Remove async context usages, avoid scope splits * Create InvalidTheme per dispatcher * Update src/Avalonia.Base/Threading/Dispatcher.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * netstandard isn't there anymore * Leftover code Removed dispatcher mock setup and test services initialization. * Remove dead code * Dispatcher provides its own time provider * Clean InputManager between tests * Update API suppressions --------- Co-authored-by: Julien Lebosquain <julien@lebosquain.net> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>pull/14481/merge
committed by
GitHub
40 changed files with 474 additions and 425 deletions
@ -0,0 +1,95 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading; |
|||
using Avalonia.Controls.Platform; |
|||
using Avalonia.Metadata; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Threading; |
|||
|
|||
public partial class Dispatcher |
|||
{ |
|||
[ThreadStatic] |
|||
private static DispatcherReferenceStorage? s_currentThreadDispatcher; |
|||
private static readonly object s_globalLock = new(); |
|||
private static readonly ConditionalWeakTable<Thread, DispatcherReferenceStorage> s_dispatchers = new(); |
|||
|
|||
private static Dispatcher? s_uiThread; |
|||
|
|||
// This class is needed PURELY for ResetForUnitTests, so we can reset s_currentThreadDispatcher for all threads
|
|||
class DispatcherReferenceStorage |
|||
{ |
|||
public WeakReference<Dispatcher> Reference = new(null!); |
|||
} |
|||
|
|||
public static Dispatcher CurrentDispatcher |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
if (s_currentThreadDispatcher?.Reference.TryGetTarget(out var dispatcher) == true) |
|||
return dispatcher; |
|||
|
|||
return new Dispatcher(null); |
|||
} |
|||
} |
|||
|
|||
public static Dispatcher? FromThread(Thread thread) |
|||
{ |
|||
lock (s_globalLock) |
|||
{ |
|||
if (s_dispatchers.TryGetValue(thread, out var reference) && reference.Reference.TryGetTarget(out var dispatcher) == true) |
|||
return dispatcher; |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static Dispatcher UIThread |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
static Dispatcher GetUIThreadDispatcherSlow() |
|||
{ |
|||
lock (s_globalLock) |
|||
{ |
|||
return s_uiThread ?? CurrentDispatcher; |
|||
} |
|||
} |
|||
return s_uiThread ?? GetUIThreadDispatcherSlow(); |
|||
} |
|||
} |
|||
|
|||
internal static Dispatcher? TryGetUIThread() |
|||
{ |
|||
lock (s_globalLock) |
|||
return s_uiThread; |
|||
} |
|||
|
|||
[PrivateApi] |
|||
public static void InitializeUIThreadDispatcher(IPlatformThreadingInterface impl) => |
|||
InitializeUIThreadDispatcher(new LegacyDispatcherImpl(impl)); |
|||
|
|||
[PrivateApi] |
|||
public static void InitializeUIThreadDispatcher(IDispatcherImpl impl) |
|||
{ |
|||
UIThread.VerifyAccess(); |
|||
if (UIThread._initialized) |
|||
throw new InvalidOperationException("UI thread dispatcher is already initialized"); |
|||
UIThread.ReplaceImplementation(impl); |
|||
} |
|||
|
|||
private static void ResetGlobalState() |
|||
{ |
|||
lock (s_globalLock) |
|||
{ |
|||
foreach (var store in s_dispatchers) |
|||
store.Value.Reference = new(null!); |
|||
s_dispatchers.Clear(); |
|||
|
|||
s_currentThreadDispatcher = null; |
|||
s_uiThread = null; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue