using System.Threading; using System.Threading.Channels; namespace Sandbox; /// /// Utility functions that revolve around the main thread /// public static class MainThread { static Channel Disposables = Channel.CreateUnbounded(); static Channel Actions = Channel.CreateUnbounded(); /// /// Wait to execute on the main thread /// public static SyncTask Wait() { return new SyncTask( SyncContext.MainThread, allowSynchronous: true ); } internal static void QueueDispose( IDisposable disposable ) { if ( disposable is null ) return; if ( ThreadSafe.IsMainThread ) { disposable.Dispose(); return; } Disposables.Writer.TryWrite( disposable ); } /// /// Run a function on the main thread and wait for the result. /// internal static T Run( int millisecondsTimeout, Func func ) { if ( ThreadSafe.IsMainThread ) return func(); T r = default; ManualResetEvent reset = new ManualResetEvent( false ); Queue( () => { try { r = func(); } finally { reset.Set(); } } ); if ( !reset.WaitOne( millisecondsTimeout ) ) { return default; } return r; } internal static void RunQueues() { ThreadSafe.AssertIsMainThread(); while ( Disposables.Reader.TryRead( out var disposable ) ) { disposable.Dispose(); } RunMainThreadQueues(); } /// /// When running in another thread you can queue a method to run in the main thread. /// If you are on the main thread we will execute the method immediately and return. /// public static void Queue( Action method ) { if ( ThreadSafe.IsMainThread ) { method(); return; } Actions.Writer.TryWrite( method ); } /// /// Run queued actions on the main thread /// internal static void RunMainThreadQueues() { ThreadSafe.AssertIsMainThread(); while ( Actions.Reader.TryRead( out var action ) ) { try { action(); } catch ( System.Exception e ) { Log.Warning( e, e.Message ); } } } }