using System; using System.Threading; namespace MinecraftClient { /// /// Holds an asynchronous task with return value /// /// Type of the return value public class TaskWithResult { private readonly AutoResetEvent resultEvent = new(false); private readonly Func task; private T? result = default; private Exception? exception = null; private bool taskRun = false; private readonly object taskRunLock = new(); /// /// 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 { get { return taskRun; } } /// /// Get the task result (return value of the inner delegate) /// /// Thrown if the task is not finished yet public T Result { get { if (taskRun) return result!; else throw new InvalidOperationException("Attempting to retrieve the result of an unfinished task"); } } /// /// Get the exception thrown by the inner delegate, if any /// public Exception? Exception { get { return exception; } } /// /// Execute the task in the current thread and set the property or to the returned value /// public void ExecuteSynchronously() { // Make sur the task will not run twice lock (taskRunLock) { if (taskRun) { throw new InvalidOperationException("Attempting to run a task twice"); } } // Run the task try { result = task(); } catch (Exception e) { exception = e; } // Mark task as complete and release wait event lock (taskRunLock) { taskRun = true; } resultEvent.Set(); } /// /// 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() { // Wait only if the result is not available yet bool mustWait = false; lock (taskRunLock) { mustWait = !taskRun; } if (mustWait) { resultEvent.WaitOne(); } // Receive exception from task if (exception != null) throw exception; return result!; } } }