using Nullframes.Threading.Coroutines;
using Nullframes.Threading.Tasks;
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Nullframes.Threading
{
public sealed partial class Dispatcher : IDisableCallback
{
#region --- Dispatch: Coroutine ---
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
///
///
public static void Invoke(IEnumerator enumerator)
{
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
Current.StartCoroutine(enumerator);
});
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
///
///
public static void Invoke(IEnumerator enumerator, ExecutionCycle cycle)
{
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
Current.StartCoroutine(enumerator);
}, cycle);
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
///
///
public static void Invoke(IEnumerator enumerator, MonoBehaviour target)
{
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
target.StartCoroutine(enumerator);
});
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
/// the execution cycle during which the passed is started.
///
///
public static void Invoke(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target)
{
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
target.StartCoroutine(enumerator);
}, cycle);
}
//--------------------------------------------------------------------------------------------------------------
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
///
/// optional cancellation token that can be passed to abort the coroutine prematurely.
///
public static void Invoke(IEnumerator enumerator, CancellationToken ct)
{
Invoke(() =>
{
if (ct.IsCancellationRequested)
{
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
Current.StartCoroutine(enumerator);
});
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
/// optional cancellation token that can be passed to abort the coroutine prematurely.
///
///
public static void Invoke(IEnumerator enumerator, ExecutionCycle cycle, CancellationToken ct)
{
Invoke(() =>
{
if (ct.IsCancellationRequested)
{
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
Current.StartCoroutine(enumerator);
}, cycle);
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
/// optional cancellation token that can be passed to abort the coroutine prematurely.
///
///
public static void Invoke(IEnumerator enumerator, MonoBehaviour target, CancellationToken ct)
{
Invoke(() =>
{
if (ct.IsCancellationRequested)
{
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
target.StartCoroutine(enumerator);
});
}
///
/// Dispatch an that will be started and executed as a on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
/// the execution cycle during which the passed is started.
/// optional cancellation token that can be passed to abort the coroutine prematurely.
///
///
public static void Invoke(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target, CancellationToken ct)
{
Invoke(() =>
{
if (ct.IsCancellationRequested)
{
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
target.StartCoroutine(enumerator);
}, cycle);
}
#endregion
//--------------------------------------------------------------------------------------------------------------
#region --- Dispatch: Coroutine Async : Await Start ---
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
var result = Current.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, MonoBehaviour target)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
var result = target.StartCoroutine(enumerator);
tcs.TrySetResult(result);
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// optional cancellation token that can be passed to abort the task prematurely.
/// exception is thrown if an is dispatched during edit mode.
/// exception is thrown if the task is cancelled prematurely.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
ct.ThrowIfCancellationRequested();
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
var result = Current.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
/// optional cancellation token that can be passed to abort the task prematurely.
/// exception is thrown if an is dispatched during edit mode.
/// exception is thrown if the task is cancelled prematurely.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, MonoBehaviour target, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource();
Invoke(() =>
{
ct.ThrowIfCancellationRequested();
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
var result = target.StartCoroutine(enumerator);
tcs.TrySetResult(result);
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, ExecutionCycle cycle)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
var result = Current.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
/// the target on which the coroutine will run.
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Coroutine)} can only be dispatched in playmode!");
return;
}
#endif
var result = target.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
/// optional cancellation token that can be passed to abort the task prematurely.
/// exception is thrown if an is dispatched during edit mode.
/// exception is thrown if the task is cancelled prematurely.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, ExecutionCycle cycle, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
ct.ThrowIfCancellationRequested();
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
var result = Current.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after it was started on the main thread.
///
/// that is started as a .
/// the execution cycle during which the passed is started.
/// the target on which the coroutine will run.
/// optional cancellation token that can be passed to abort the task prematurely.
/// exception is thrown if an is dispatched during edit mode.
/// exception is thrown if the task is cancelled prematurely.
///
public static Task InvokeAsyncAwaitStart(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource();
Invoke(() =>
{
ct.ThrowIfCancellationRequested();
#if UNITY_EDITOR
if (!Application.isPlaying)
{
throw new InvalidOperationException($"{nameof(Coroutine)} can only be dispatched in playmode!");
}
#endif
var result = target.StartCoroutine(enumerator);
tcs.TrySetResult(result);
}, cycle);
return tcs.Task;
}
#endregion
//--------------------------------------------------------------------------------------------------------------
#region --- Dispatch: Coroutine Async : Await Completion ---
///
/// Dispatch an that is executed as a
/// on the main thread and return a , that can be awaited and returns
/// after the has completed on the main thread.
///
/// that is started as a .
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, CancellationToken.None, throwExceptions);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
///
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, CancellationToken ct, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, ct, throwExceptions);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, MonoBehaviour target, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, CancellationToken.None, throwExceptions, target);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
/// the target on which the coroutine will run.
///
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, MonoBehaviour target, CancellationToken ct, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, ct, throwExceptions, target);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
});
return tcs.Task;
}
//--------------------------------------------------------------------------------------------------------------
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
///
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, ExecutionCycle cycle, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, CancellationToken.None, throwExceptions);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
///
///
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, ExecutionCycle cycle, CancellationToken ct, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, ct, throwExceptions);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
///
/// the target on which the coroutine will run.
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, CancellationToken.None, throwExceptions, target);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
///
/// Dispatch an that will be started and executed as a
/// on the main thread; and return a , that when awaited on the calling thread, returns
/// the after the has completed on the main thread.
///
/// that is started as a .
///
/// the target on which the coroutine will run.
/// Optional cancellation token
///
/// exception is thrown if an is dispatched during edit mode.
///
public static Task InvokeAsyncAwaitCompletion(IEnumerator enumerator, ExecutionCycle cycle, MonoBehaviour target, CancellationToken ct, bool throwExceptions = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
StartCoroutineInternal(enumerator, tcs, ct, throwExceptions, target);
}
catch (Exception exception)
{
tcs.TrySetException(exception);
}
}, cycle);
return tcs.Task;
}
#endregion
//--------------------------------------------------------------------------------------------------------------
#region --- Stop Coroutine ---
///
/// Stop a coroutine that is running on the Dispatcher.
///
/// The coroutine that should be cancelled
public static void CancelCoroutine(Coroutine coroutine)
{
Invoke(() =>
{
Current.StopCoroutine(coroutine);
});
}
///
/// Stop a coroutine that is running on the Dispatcher and return a Task that when awaited will yield after
/// the coroutine was successfully stopped on the main thread.
///
/// The coroutine that should be cancelled
public static Task CancelCoroutineAsync(Coroutine coroutine)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
Current.StopCoroutine(coroutine);
tcs.SetCompleted();
}
catch (Exception exception)
{
tcs.SetException(exception);
}
});
return tcs.Task;
}
///
/// Stop a coroutine that is running on the Dispatcher and return a Task that when awaited will yield after
/// the coroutine was successfully stopped on the main thread.
///
/// The coroutine that should be cancelled
/// Optional cancellation token
/// Determines if an exception should be thrown on cancellation
public static Task CancelCoroutineAsync(Coroutine coroutine, CancellationToken ct, bool throwOnCancellation = true)
{
var tcs = new TaskCompletionSource();
Invoke(() =>
{
try
{
ct.ThrowIfCancellationRequested();
Current.StopCoroutine(coroutine);
tcs.SetCompleted();
}
catch (OperationCanceledException oce)
{
if (throwOnCancellation)
{
tcs.SetException(oce);
}
else
{
tcs.SetCompleted();
}
}
catch (Exception exception)
{
tcs.SetException(exception);
}
});
return tcs.Task;
}
#endregion
//--------------------------------------------------------------------------------------------------------------
#region --- Internal Coroutine ---
///
/// Start an internal coroutine with cancellation support and exception handling.
/// z
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StartCoroutineInternal(IEnumerator coroutine, TaskCompletionSource tcs, CancellationToken ct, bool throwExceptions, MonoBehaviour target)
{
if (throwExceptions)
{
target.StartCoroutineExceptionSensitive(coroutine, tcs.TrySetException, tcs.TrySetCompleted, ct);
}
else
{
target.StartCoroutineExceptionSensitive(coroutine, tcs.TrySetResult, tcs.TrySetCompleted, ct);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StartCoroutineInternal(IEnumerator coroutine, TaskCompletionSource tcs, CancellationToken ct, bool throwExceptions)
{
if (throwExceptions)
{
Current.StartCoroutineExceptionSensitive(coroutine, tcs.TrySetException, tcs.TrySetCompleted, ct, current);
}
else
{
Current.StartCoroutineExceptionSensitive(coroutine, tcs.TrySetResult, tcs.TrySetCompleted, ct, current);
}
}
#endregion
#region --- Interface: IDisableCallback---
///
/// Event is be invoked when the target behaviour is disabled or destroyed.
///
public event Action Disabled;
private void OnDisable()
{
Disabled?.Invoke();
Disabled = null;
}
#endregion
}
}