(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Describes the Tasks State
|
||||
/// </summary>
|
||||
public enum TaskStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Working
|
||||
/// </summary>
|
||||
Pending,
|
||||
/// <summary>
|
||||
/// Exception as thrown or otherwise stopped early
|
||||
/// </summary>
|
||||
Faulted,
|
||||
/// <summary>
|
||||
/// Complete without error
|
||||
/// </summary>
|
||||
Success,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution strategy for the Task
|
||||
/// </summary>
|
||||
public enum TaskStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatches the task to a background thread
|
||||
/// </summary>
|
||||
BackgroundThread,
|
||||
/// <summary>
|
||||
/// Dispatches the task to the main thread
|
||||
/// </summary>
|
||||
MainThread,
|
||||
/// <summary>
|
||||
/// Dispatches the task to the current thread
|
||||
/// </summary>
|
||||
CurrentThread,
|
||||
/// <summary>
|
||||
/// Runs the task as a coroutine
|
||||
/// </summary>
|
||||
Coroutine,
|
||||
/// <summary>
|
||||
/// Does nothing. For custom tasks.
|
||||
/// </summary>
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// 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)
|
||||
///</code>
|
||||
///</example>
|
||||
public partial class AsyncTask :
|
||||
#if UNITY_5
|
||||
CustomYieldInstruction,
|
||||
#endif
|
||||
IDisposable
|
||||
{
|
||||
#region options
|
||||
/// <summary>
|
||||
/// Forces use of a single thread for debugging
|
||||
/// </summary>
|
||||
public static bool DisableMultiThread = false;
|
||||
|
||||
/// <summary>
|
||||
/// Logs Exceptions
|
||||
/// </summary>
|
||||
public static bool LogErrors = false;
|
||||
#endregion
|
||||
|
||||
#region properties
|
||||
|
||||
/// <summary>
|
||||
/// Run execution path
|
||||
/// </summary>
|
||||
public TaskStrategy Strategy;
|
||||
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// </summary>
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Run State
|
||||
/// </summary>
|
||||
public TaskStatus Status { get; set; }
|
||||
|
||||
#if UNITY_5
|
||||
/// <summary>
|
||||
/// Custom Yield
|
||||
/// </summary>
|
||||
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<Delegate> _completeList;
|
||||
|
||||
#endregion
|
||||
|
||||
#region constructor
|
||||
|
||||
static AsyncTask()
|
||||
{
|
||||
#if UNITY
|
||||
TaskManager.ConfirmInit();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new task
|
||||
/// </summary>
|
||||
public AsyncTask()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new task
|
||||
/// </summary>
|
||||
public AsyncTask(TaskStrategy mode)
|
||||
{
|
||||
Strategy = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Task in a Faulted state
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
public AsyncTask(Exception ex)
|
||||
{
|
||||
Exception = ex;
|
||||
Strategy = TaskStrategy.Custom;
|
||||
Status = TaskStatus.Faulted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new background task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public AsyncTask(Action action)
|
||||
{
|
||||
_action = action;
|
||||
Strategy = TaskStrategy.BackgroundThread;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="mode"></param>
|
||||
public AsyncTask(Action action, TaskStrategy mode)
|
||||
: this()
|
||||
{
|
||||
if (mode == TaskStrategy.Coroutine)
|
||||
throw new ArgumentException("Action tasks may not be coroutines");
|
||||
|
||||
_action = action;
|
||||
Strategy = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Coroutine Task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Runs complete logic, for custom tasks
|
||||
/// </summary>
|
||||
public virtual void Complete(Exception ex = null)
|
||||
{
|
||||
if (ex == null)
|
||||
{
|
||||
Exception = null;
|
||||
Status = TaskStatus.Success;
|
||||
OnTaskComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception = ex;
|
||||
Status = TaskStatus.Faulted;
|
||||
OnTaskComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the task
|
||||
/// </summary>
|
||||
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<Delegate>();
|
||||
}
|
||||
|
||||
_completeList.Add(action);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#if UNITY
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
public static class TaskExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// will throw if faulted
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static T ThrowIfFaulted<T>(this T self) where T : AsyncTask
|
||||
{
|
||||
if (self.IsFaulted)
|
||||
throw self.Exception;
|
||||
return self;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the task to complete
|
||||
/// </summary>
|
||||
public static T ContinueWith<T>(this T self, Action<T> continuation) where T : AsyncTask
|
||||
{
|
||||
if (self.IsCompleted)
|
||||
{
|
||||
continuation(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.AddContinue(continuation);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a timeout to the task. Will raise an exception if still running
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="seconds"></param>
|
||||
/// <param name="onTimeout"></param>
|
||||
/// <returns></returns>
|
||||
public static T AddTimeout<T>(this T self, int seconds, Action<AsyncTask> onTimeout = null) where T : AsyncTask
|
||||
{
|
||||
TaskManager.StartRoutine(TimeOutAsync(self, seconds, onTimeout));
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static IEnumerator TimeOutAsync(AsyncTask task, int seconds, Action<AsyncTask> onTimeout = null)
|
||||
{
|
||||
yield return new WaitForSeconds(seconds);
|
||||
|
||||
if (task.IsRunning)
|
||||
{
|
||||
if (onTimeout != null)
|
||||
{
|
||||
onTimeout(task);
|
||||
}
|
||||
|
||||
task.Complete(new Exception("Timeout"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// 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)
|
||||
///</code>
|
||||
///</example>
|
||||
public partial class AsyncTask
|
||||
{
|
||||
#region Task
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask Run(Action action)
|
||||
{
|
||||
var task = new AsyncTask(action);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask RunOnMain(Action action)
|
||||
{
|
||||
var task = new AsyncTask(action, TaskStrategy.MainThread);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask RunOnCurrent(Action action)
|
||||
{
|
||||
var task = new AsyncTask(action, TaskStrategy.CurrentThread);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Coroutine
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask RunCoroutine(IEnumerator function)
|
||||
{
|
||||
var task = new AsyncTask(function);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask RunCoroutine(Func<IEnumerator> function)
|
||||
{
|
||||
var task = new AsyncTask(function());
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask RunCoroutine(Func<AsyncTask, IEnumerator> function)
|
||||
{
|
||||
var task = new AsyncTask();
|
||||
task.Strategy = TaskStrategy.Coroutine;
|
||||
task._routine = function(task);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if UNITY
|
||||
#region Task With Result
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask<TResult> Run<TResult>(Func<TResult> function)
|
||||
{
|
||||
var task = new AsyncTask<TResult>(function);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask<TResult> RunOnMain<TResult>(Func<TResult> function)
|
||||
{
|
||||
var task = new AsyncTask<TResult>(function, TaskStrategy.MainThread);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask<TResult> RunOnCurrent<TResult>(Func<TResult> function)
|
||||
{
|
||||
var task = new AsyncTask<TResult>(function, TaskStrategy.CurrentThread);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new running task
|
||||
/// </summary>
|
||||
public static AsyncTask<TResult> RunCoroutine<TResult>(IEnumerator function)
|
||||
{
|
||||
var task = new AsyncTask<TResult>(function);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a task which passes the task as a parameter
|
||||
/// </summary>
|
||||
public static AsyncTask<TResult> RunCoroutine<TResult>(Func<AsyncTask<TResult>, IEnumerator> function)
|
||||
{
|
||||
var task = new AsyncTask<TResult>();
|
||||
task.Strategy = TaskStrategy.Coroutine;
|
||||
task._routine = function(task);
|
||||
task.Start();
|
||||
return task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region success / fails
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the success state
|
||||
/// </summary>
|
||||
static AsyncTask _successTask = new AsyncTask(TaskStrategy.Custom) { Status = TaskStatus.Success };
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the success state
|
||||
/// </summary>
|
||||
public static AsyncTask<T> SuccessTask<T>(T result)
|
||||
{
|
||||
return new AsyncTask<T>(TaskStrategy.Custom) { Status = TaskStatus.Success, Result = result };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the faulted state
|
||||
/// </summary>
|
||||
public static AsyncTask SuccessTask()
|
||||
{
|
||||
return _successTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the faulted state
|
||||
/// </summary>
|
||||
public static AsyncTask FailedTask(string exception)
|
||||
{
|
||||
return FailedTask(new Exception(exception));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the faulted state
|
||||
/// </summary>
|
||||
public static AsyncTask FailedTask(Exception ex)
|
||||
{
|
||||
return new AsyncTask(TaskStrategy.Custom) { Status = TaskStatus.Faulted, Exception = ex };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the faulted state
|
||||
/// </summary>
|
||||
public static AsyncTask<T> FailedTask<T>(string exception)
|
||||
{
|
||||
return FailedTask<T>(new Exception(exception));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default task in the faulted state
|
||||
/// </summary>
|
||||
public static AsyncTask<T> FailedTask<T>(Exception ex)
|
||||
{
|
||||
return new AsyncTask<T>(TaskStrategy.Custom) { Status = TaskStatus.Faulted, Exception = ex };
|
||||
}
|
||||
#endregion
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# if !WINDOWS_WSA && !WINDOWS_UWP
|
||||
using System.Threading;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
public partial class TaskManager
|
||||
{ /// <summary>
|
||||
/// Checks if this is the main thread
|
||||
/// </summary>
|
||||
public static bool IsMainThread
|
||||
{
|
||||
get { return Thread.CurrentThread == MainThread; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Main Thread
|
||||
/// </summary>
|
||||
public static Thread MainThread { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Current Thread
|
||||
/// </summary>
|
||||
public static Thread CurrentThread
|
||||
{
|
||||
get
|
||||
{
|
||||
return Thread.CurrentThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#if WINDOWS_WSA || WINDOWS_UWP
|
||||
using System;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
public partial class TaskManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if this is the main thread
|
||||
/// </summary>
|
||||
public static bool IsMainThread
|
||||
{
|
||||
get { return Environment.CurrentManagedThreadId == MainThread; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Main Thread
|
||||
/// </summary>
|
||||
public static int MainThread { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Current Thread
|
||||
/// </summary>
|
||||
public static int CurrentThread
|
||||
{
|
||||
get { return Environment.CurrentManagedThreadId; }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,308 @@
|
||||
#if UNITY
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Manager for running coroutines and scheduling actions to runs in the main thread.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Self instantiating. No need to add to scene.
|
||||
/// </remarks>
|
||||
[AddComponentMenu("Foundation/TaskManager")]
|
||||
[ExecuteInEditMode]
|
||||
public partial class TaskManager : MonoBehaviour
|
||||
{
|
||||
|
||||
#region sub
|
||||
/// <summary>
|
||||
/// Thread Safe logger command
|
||||
/// </summary>
|
||||
public struct LogCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Color Code
|
||||
/// </summary>
|
||||
public LogType Type;
|
||||
/// <summary>
|
||||
/// Text
|
||||
/// </summary>
|
||||
public object Message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thread safe coroutine command
|
||||
/// </summary>
|
||||
public struct CoroutineCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// The IEnumerator Coroutine
|
||||
/// </summary>
|
||||
public IEnumerator Coroutine;
|
||||
/// <summary>
|
||||
/// Called on complete
|
||||
/// </summary>
|
||||
public Action OnComplete;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Static Accessor
|
||||
/// </summary>
|
||||
public static TaskManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
ConfirmInit();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirms the instance is ready for use
|
||||
/// </summary>
|
||||
public static void ConfirmInit()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
var old = FindObjectsOfType<TaskManager>();
|
||||
foreach (var manager in old)
|
||||
{
|
||||
if (Application.isEditor)
|
||||
DestroyImmediate(manager.gameObject);
|
||||
else
|
||||
Destroy(manager.gameObject);
|
||||
}
|
||||
|
||||
|
||||
var go = new GameObject("_TaskManager");
|
||||
DontDestroyOnLoad(go);
|
||||
_instance = go.AddComponent<TaskManager>();
|
||||
|
||||
MainThread = CurrentThread;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scheduled the routine to run (on the main thread)
|
||||
/// </summary>
|
||||
public static Coroutine WaitForSeconds(int seconds)
|
||||
{
|
||||
return Instance.StartCoroutine(Instance.WaitForSecondsInternal(seconds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scheduled the routine to run (on the main thread)
|
||||
/// </summary>
|
||||
public static Coroutine StartRoutine(IEnumerator coroutine)
|
||||
{
|
||||
if (IsApplicationQuit)
|
||||
return null;
|
||||
|
||||
//Make sure we are in the main thread
|
||||
if (!IsMainThread)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
PendingAdd.Add(coroutine);
|
||||
|
||||
//Debug.LogWarning("Running coroutines from background thread are not awaitable. Use CoroutineInfo");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return Instance.StartCoroutine(coroutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scheduled the routine to run (on the main thread)
|
||||
/// </summary>
|
||||
public static void StartRoutine(CoroutineCommand info)
|
||||
{
|
||||
if (IsApplicationQuit)
|
||||
return;
|
||||
|
||||
//Make sure we are in the main thread
|
||||
if (!IsMainThread)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
PendingCoroutineInfo.Add(info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.StartCoroutine(Instance.RunCoroutineInfo(info));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scheduled the routine to run (on the main thread)
|
||||
/// </summary>
|
||||
public static void StopRoutine(IEnumerator coroutine)
|
||||
{
|
||||
if (IsApplicationQuit)
|
||||
return;
|
||||
|
||||
//Make sure we are in the main thread
|
||||
if (!IsMainThread)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
PendingRemove.Add(coroutine);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.StopCoroutine(coroutine);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedules the action to run on the main thread
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public static void RunOnMainThread(Action action)
|
||||
{
|
||||
if (IsApplicationQuit)
|
||||
return;
|
||||
|
||||
//Make sure we are in the main thread
|
||||
if (!IsMainThread)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
PendingActions.Add(action);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A thread safe logger
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
public static void Log(LogCommand m)
|
||||
{
|
||||
if (!IsMainThread)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
PendingLogs.Add(m);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(m);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void Write(LogCommand m)
|
||||
{
|
||||
switch (m.Type)
|
||||
{
|
||||
case LogType.Warning:
|
||||
Debug.LogWarning(m.Message);
|
||||
break;
|
||||
case LogType.Error:
|
||||
case LogType.Exception:
|
||||
Debug.LogError(m.Message);
|
||||
break;
|
||||
case LogType.Log:
|
||||
case LogType.Assert:
|
||||
Debug.Log(m.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static TaskManager _instance;
|
||||
private static object syncRoot = new object();
|
||||
protected static readonly List<CoroutineCommand> PendingCoroutineInfo = new List<CoroutineCommand>();
|
||||
protected static readonly List<IEnumerator> PendingAdd = new List<IEnumerator>();
|
||||
protected static readonly List<IEnumerator> PendingRemove = new List<IEnumerator>();
|
||||
protected static readonly List<Action> PendingActions = new List<Action>();
|
||||
protected static readonly List<LogCommand> PendingLogs = new List<LogCommand>();
|
||||
protected static bool IsApplicationQuit;
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected void Update()
|
||||
{
|
||||
if (IsApplicationQuit)
|
||||
return;
|
||||
|
||||
if (PendingAdd.Count == 0 && PendingRemove.Count == 0 && PendingActions.Count == 0 && PendingLogs.Count == 0 && PendingCoroutineInfo.Count == 0)
|
||||
return;
|
||||
|
||||
lock (syncRoot)
|
||||
{
|
||||
for (int i = 0;i < PendingLogs.Count;i++)
|
||||
{
|
||||
Write(PendingLogs[i]);
|
||||
}
|
||||
for (int i = 0;i < PendingAdd.Count;i++)
|
||||
{
|
||||
StartCoroutine(PendingAdd[i]);
|
||||
}
|
||||
for (int i = 0;i < PendingRemove.Count;i++)
|
||||
{
|
||||
StopCoroutine(PendingRemove[i]);
|
||||
}
|
||||
for (int i = 0;i < PendingCoroutineInfo.Count;i++)
|
||||
{
|
||||
StartCoroutine(RunCoroutineInfo(PendingCoroutineInfo[i]));
|
||||
}
|
||||
for (int i = 0;i < PendingActions.Count;i++)
|
||||
{
|
||||
PendingActions[i]();
|
||||
}
|
||||
PendingAdd.Clear();
|
||||
PendingRemove.Clear();
|
||||
PendingActions.Clear();
|
||||
PendingLogs.Clear();
|
||||
PendingCoroutineInfo.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator RunCoroutineInfo(CoroutineCommand info)
|
||||
{
|
||||
yield return StartCoroutine(info.Coroutine);
|
||||
|
||||
if (info.OnComplete != null)
|
||||
info.OnComplete();
|
||||
}
|
||||
|
||||
protected void OnApplicationQuit()
|
||||
{
|
||||
IsApplicationQuit = true;
|
||||
}
|
||||
|
||||
IEnumerator WaitForSecondsInternal(int seconds)
|
||||
{
|
||||
if(seconds <= 0)
|
||||
yield break;
|
||||
|
||||
var delta = 0f;
|
||||
|
||||
while (delta < seconds)
|
||||
{
|
||||
delta += Time.unscaledDeltaTime;
|
||||
yield return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
#if UNITY
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Foundation.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// 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)
|
||||
///</code>
|
||||
///</example>
|
||||
public class AsyncTask<TResult> : AsyncTask
|
||||
{
|
||||
#region public fields
|
||||
|
||||
/// <summary>
|
||||
/// get the result of the task. Blocking. It is recommended you yield on the wait before accessing this value
|
||||
/// </summary>
|
||||
public TResult Result;
|
||||
#endregion
|
||||
|
||||
#region ctor
|
||||
|
||||
Func<TResult> _function;
|
||||
|
||||
public AsyncTask()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the task in the Success state.
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
public AsyncTask(TResult result)
|
||||
: this()
|
||||
{
|
||||
Status = TaskStatus.Success;
|
||||
Strategy = TaskStrategy.Custom;
|
||||
Result = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new background Task strategy
|
||||
/// </summary>
|
||||
/// <param name="function"></param>
|
||||
public AsyncTask(Func<TResult> function)
|
||||
: this()
|
||||
{
|
||||
if (function == null)
|
||||
throw new ArgumentNullException("function");
|
||||
|
||||
_function = function;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new task with a specific strategy
|
||||
/// </summary>
|
||||
public AsyncTask(Func<TResult> function, TaskStrategy mode)
|
||||
: this()
|
||||
{
|
||||
if (function == null)
|
||||
throw new ArgumentNullException("function");
|
||||
|
||||
if (mode == TaskStrategy.Coroutine)
|
||||
throw new ArgumentException("Mode can not be coroutine");
|
||||
|
||||
_function = function;
|
||||
Strategy = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Coroutine task
|
||||
/// </summary>
|
||||
public AsyncTask(IEnumerator routine)
|
||||
{
|
||||
if (routine == null)
|
||||
throw new ArgumentNullException("routine");
|
||||
|
||||
|
||||
_routine = routine;
|
||||
Strategy = TaskStrategy.Coroutine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Task in a Faulted state
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
public AsyncTask(Exception ex)
|
||||
{
|
||||
Exception = ex;
|
||||
Strategy = TaskStrategy.Custom;
|
||||
Status = TaskStatus.Faulted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new task
|
||||
/// </summary>
|
||||
public AsyncTask(TaskStrategy mode)
|
||||
: this()
|
||||
{
|
||||
Strategy = mode;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region protected methods
|
||||
|
||||
/// <summary>
|
||||
/// Runs complete logic, for custom tasks
|
||||
/// </summary>
|
||||
public override void Complete(Exception ex = null)
|
||||
{
|
||||
Result = default(TResult);
|
||||
base.Complete(ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs complete logic, for custom tasks
|
||||
/// </summary>
|
||||
public void Complete(TResult result)
|
||||
{
|
||||
Result = result;
|
||||
base.Complete();
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
Result = default(TResult);
|
||||
base.Start();
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_function != null)
|
||||
{
|
||||
Result = _function();
|
||||
}
|
||||
Status = TaskStatus.Success;
|
||||
OnTaskComplete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception = ex;
|
||||
Status = TaskStatus.Faulted;
|
||||
if (LogErrors)
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user