using Sandbox.Engine; using Sandbox.Utility; using System.Threading; namespace Sandbox.Tasks; internal static partial class SyncContext { /// /// Current sync context for the main thread. This will be null until has been /// called for the first time. /// public static ExpirableSynchronizationContext MainThread { get; private set; } /// /// Current sync context for worker threads. This will be null until has been /// called for the first time. /// public static ExpirableSynchronizationContext WorkerThread { get; private set; } /// /// Sets both and to be a new /// instance of . Only has an effect the first time it's called. /// public static void Init() { if ( MainThread != null ) return; MainThread = new ExpirableSynchronizationContext( false ); WorkerThread = new ExpirableSynchronizationContext( true ); SynchronizationContext.SetSynchronizationContext( MainThread ); } /// /// Invalidates and , and replaces /// them with a new instance. /// Any tasks that try to continue on the old instances will log an error, unless they /// are whitelisted with . /// public static void Reset() { MainThread = Reset( MainThread ); WorkerThread = Reset( WorkerThread ); } private static ExpirableSynchronizationContext Reset( ExpirableSynchronizationContext oldInstance ) { var newInstance = new ExpirableSynchronizationContext( oldInstance.WarnNonYieldingTasks ); if ( SynchronizationContext.Current == oldInstance ) { SynchronizationContext.SetSynchronizationContext( newInstance ); } oldInstance?.Expire( newInstance ); newInstance.ProcessQueue(); return newInstance; } /// /// Run an async task in a synchronous blocking manner. /// public static void RunBlocking( Task task ) { ThreadSafe.AssertIsMainThread(); while ( !task.IsCompleted ) { EngineLoop.RunAsyncTasks(); Thread.Yield(); IToolsDll.Current?.Spin(); } if ( task.Exception != null ) throw task.Exception; } /// /// Run an async task in a synchronous blocking manner and returns the result. /// public static TResult RunBlocking( Task task ) { ThreadSafe.AssertIsMainThread(); while ( !task.IsCompleted ) { EngineLoop.RunAsyncTasks(); Thread.Yield(); } if ( task.Exception != null ) throw task.Exception; return task.Result; } /// /// Create a scope that sets the current synchronization context to the provided context. /// internal static IDisposable Scope( SynchronizationContext context ) { var oldContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext( context ); return DisposeAction.Create( () => { SynchronizationContext.SetSynchronizationContext( oldContext ); } ); } }