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