using System; using System.Threading; using System.Threading.Tasks; namespace MinecraftClient { internal interface IMainThreadTask { void ExecuteSynchronously(); void Cancel(); } /// /// Holds an asynchronous task with return value /// /// Type of the return value public sealed class TaskWithResult : IMainThreadTask { private readonly Func task; private readonly TaskCompletionSource completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); private int taskState; /// /// Create a new asynchronous task with return value /// /// Delegate with return value public TaskWithResult(Func task) { this.task = task; } /// /// Check whether the task has finished running /// public bool HasRun => completionSource.Task.IsCompleted; /// /// Get the task result (return value of the inner delegate) /// /// Thrown if the task is not finished yet public T Result { get { if (!completionSource.Task.IsCompleted) throw new InvalidOperationException("Attempting to retrieve the result of an unfinished task"); return completionSource.Task.GetAwaiter().GetResult(); } } /// /// Get the exception thrown by the inner delegate, if any /// public Exception? Exception { get { return completionSource.Task.Exception?.InnerException; } } public Task AsTask() { return completionSource.Task; } /// /// Execute the task in the current thread and set the property or to the returned value /// public void ExecuteSynchronously() { if (Interlocked.CompareExchange(ref taskState, 1, 0) != 0) throw new InvalidOperationException("Attempting to run a task twice"); try { completionSource.TrySetResult(task()); } catch (Exception e) { completionSource.TrySetException(e); } } public void Cancel() { if (Interlocked.CompareExchange(ref taskState, 1, 0) != 0) return; completionSource.TrySetException(new OperationCanceledException("Main-thread task was canceled before execution.")); } /// /// Wait until the task has run from another thread and get the returned value or exception thrown by the task /// /// Task result once available /// Any exception thrown by the task public T WaitGetResult() { return completionSource.Task.GetAwaiter().GetResult(); } } }