using System;
using System.Collections;
using System.Collections.Generic;
#if UNITY
using UnityEngine;
#endif
#if WINDOWS_WSA || WINDOWS_UWP
using Windows.System.Threading;
#else
using System.Threading;
#endif
namespace Foundation.Tasks
{
///
/// Describes the Tasks State
///
public enum TaskStatus
{
///
/// Working
///
Pending,
///
/// Exception as thrown or otherwise stopped early
///
Faulted,
///
/// Complete without error
///
Success,
}
///
/// Execution strategy for the Task
///
public enum TaskStrategy
{
///
/// Dispatches the task to a background thread
///
BackgroundThread,
///
/// Dispatches the task to the main thread
///
MainThread,
///
/// Dispatches the task to the current thread
///
CurrentThread,
///
/// Runs the task as a coroutine
///
Coroutine,
///
/// Does nothing. For custom tasks.
///
Custom,
}
///
/// A task encapsulates future work that may be waited on.
/// - Support running actions in background threads
/// - Supports running coroutines with return results
/// - Use the WaitForRoutine method to wait for the task in a coroutine
///
///
///
/// var task = Task.Run(() =>
/// {
/// //Debug.Log does not work in
/// Debug.Log("Sleeping...");
/// Task.Delay(2000);
/// Debug.Log("Slept");
/// });
/// // wait for it
/// yield return task;
///
/// // check exceptions
/// if(task.IsFaulted)
/// Debug.LogException(task.Exception)
///
///
public partial class AsyncTask :
#if UNITY_5
CustomYieldInstruction,
#endif
IDisposable
{
#region options
///
/// Forces use of a single thread for debugging
///
public static bool DisableMultiThread = false;
///
/// Logs Exceptions
///
public static bool LogErrors = false;
#endregion
#region properties
///
/// Run execution path
///
public TaskStrategy Strategy;
///
/// Error
///
public Exception Exception { get; set; }
///
/// Run State
///
public TaskStatus Status { get; set; }
#if UNITY_5
///
/// Custom Yield
///
public override bool keepWaiting
{
get { return !IsCompleted; }
}
#endif
public bool IsRunning
{
get { return Status == TaskStatus.Pending; }
}
public bool IsCompleted
{
get { return (Status == TaskStatus.Success || Status == TaskStatus.Faulted) && !HasContinuations; }
}
public bool IsFaulted
{
get { return Status == TaskStatus.Faulted; }
}
public bool IsSuccess
{
get { return Status == TaskStatus.Success; }
}
public bool HasContinuations { get; protected set; }
#endregion
#region private
protected TaskStatus _status;
protected Action _action;
protected IEnumerator _routine;
List _completeList;
#endregion
#region constructor
static AsyncTask()
{
#if UNITY
TaskManager.ConfirmInit();
#endif
}
///
/// Creates a new task
///
public AsyncTask()
{
}
///
/// Creates a new task
///
public AsyncTask(TaskStrategy mode)
{
Strategy = mode;
}
///
/// Creates a new Task in a Faulted state
///
///
public AsyncTask(Exception ex)
{
Exception = ex;
Strategy = TaskStrategy.Custom;
Status = TaskStatus.Faulted;
}
///
/// Creates a new background task
///
///
public AsyncTask(Action action)
{
_action = action;
Strategy = TaskStrategy.BackgroundThread;
}
///
/// Creates a new Task
///
///
///
public AsyncTask(Action action, TaskStrategy mode)
: this()
{
if (mode == TaskStrategy.Coroutine)
throw new ArgumentException("Action tasks may not be coroutines");
_action = action;
Strategy = mode;
}
///
/// Creates a new Coroutine Task
///
///
public AsyncTask(IEnumerator action)
: this()
{
if (action == null)
throw new ArgumentNullException("action");
_routine = action;
Strategy = TaskStrategy.Coroutine;
}
#endregion
#region Private
protected virtual void Execute()
{
try
{
if (_action != null)
{
_action();
}
Status = TaskStatus.Success;
OnTaskComplete();
}
catch (Exception ex)
{
Exception = ex;
Status = TaskStatus.Faulted;
#if UNITY
if (LogErrors)
Debug.LogException(ex);
#endif
}
}
#if WINDOWS_WSA || WINDOWS_UWP
protected async void RunOnBackgroundThread()
{
Status = TaskStatus.Pending;
await ThreadPool.RunAsync(o => Execute());
#else
protected void RunOnBackgroundThread()
{
Status = TaskStatus.Pending;
ThreadPool.QueueUserWorkItem(state => Execute());
#endif
}
protected void RunOnCurrentThread()
{
Status = TaskStatus.Pending;
Execute();
}
#if UNITY
protected void RunOnMainThread()
{
Status = TaskStatus.Pending;
TaskManager.RunOnMainThread(Execute);
}
protected void RunAsCoroutine()
{
Status = TaskStatus.Pending;
TaskManager.StartRoutine(new TaskManager.CoroutineCommand
{
Coroutine = _routine,
OnComplete = OnRoutineComplete
});
}
#endif
protected virtual void OnTaskComplete()
{
if (_completeList != null)
{
foreach (var d in _completeList)
{
if (d != null)
d.DynamicInvoke(this);
}
_completeList = null;
}
HasContinuations = false;
}
protected void OnRoutineComplete()
{
if (Status == TaskStatus.Pending)
{
Status = TaskStatus.Success;
OnTaskComplete();
}
}
#endregion
#region public methods
///
/// Runs complete logic, for custom tasks
///
public virtual void Complete(Exception ex = null)
{
if (ex == null)
{
Exception = null;
Status = TaskStatus.Success;
OnTaskComplete();
}
else
{
Exception = ex;
Status = TaskStatus.Faulted;
OnTaskComplete();
}
}
///
/// Executes the task
///
public virtual void Start()
{
Status = TaskStatus.Pending;
switch (Strategy)
{
case TaskStrategy.Custom:
break;
#if UNITY
case TaskStrategy.Coroutine:
RunAsCoroutine();
break;
#endif
case TaskStrategy.BackgroundThread:
if (DisableMultiThread)
RunOnCurrentThread();
else
RunOnBackgroundThread();
break;
case TaskStrategy.CurrentThread:
RunOnCurrentThread();
break;
#if UNITY
case TaskStrategy.MainThread:
RunOnMainThread();
break;
#endif
}
}
public virtual void Dispose()
{
Status = TaskStatus.Pending;
Exception = null;
_action = null;
_routine = null;
_completeList = null;
HasContinuations = false;
}
public void AddContinue(Delegate action)
{
HasContinuations = true;
if (_completeList == null)
{
_completeList = new List();
}
_completeList.Add(action);
}
#endregion
}
}