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 } }