Improve InvokeOnMainThread mechanism

Add documentation to make the invoke mechanism easier to understand
Make it clear in documentation that code is invoked synchronously
Use Action and Func<T> for minimizing the amount of code to write
Use type parameter T to automatically adjust return value type
Throw exceptions on the calling thread, not the main thread
This commit is contained in:
ORelio 2021-05-15 16:31:02 +02:00
parent 9e5364a4ff
commit c1cfaf520d
6 changed files with 164 additions and 59 deletions

View file

@ -6,42 +6,121 @@ using System.Threading;
namespace MinecraftClient
{
public class TaskWithResult
/// <summary>
/// Holds an asynchronous task with return value
/// </summary>
/// <typeparam name="T">Type of the return value</typeparam>
public class TaskWithResult<T>
{
private Delegate Task;
private AutoResetEvent ResultEvent = new AutoResetEvent(false);
private AutoResetEvent resultEvent = new AutoResetEvent(false);
private Func<T> task;
private T result = default(T);
private Exception exception = null;
private bool taskRun = false;
private object taskRunLock = new object();
public object Result;
public TaskWithResult(Delegate task)
/// <summary>
/// Create a new asynchronous task with return value
/// </summary>
/// <param name="task">Delegate with return value</param>
public TaskWithResult(Func<T> task)
{
Task = task;
this.task = task;
}
/// <summary>
/// Execute the delegate and set the <see cref="Result"/> property to the returned value
/// Check whether the task has finished running
/// </summary>
/// <returns>Value returned from delegate</returns>
public object Execute()
public bool HasRun
{
Result = Task.DynamicInvoke();
return Result;
get
{
return taskRun;
}
}
/// <summary>
/// Block the program execution
/// Get the task result (return value of the inner delegate)
/// </summary>
public void Block()
/// <exception cref="System.InvalidOperationException">Thrown if the task is not finished yet</exception>
public T Result
{
ResultEvent.WaitOne();
get
{
if (taskRun)
{
return result;
}
else throw new InvalidOperationException("Attempting to retrieve the result of an unfinished task");
}
}
/// <summary>
/// Resume the program execution
/// Get the exception thrown by the inner delegate, if any
/// </summary>
public void Release()
public Exception Exception
{
ResultEvent.Set();
get
{
return exception;
}
}
/// <summary>
/// Execute the task in the current thread and set the <see cref="Result"/> property or <see cref=""/>to the returned value
/// </summary>
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();
}
/// <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()
{
// 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;
}
}
}