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 ); } );
}
}