#if UNITY
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Foundation.Tasks
{
///
/// Manager for running coroutines and scheduling actions to runs in the main thread.
///
///
/// Self instantiating. No need to add to scene.
///
[AddComponentMenu("Foundation/TaskManager")]
[ExecuteInEditMode]
public partial class TaskManager : MonoBehaviour
{
#region sub
///
/// Thread Safe logger command
///
public struct LogCommand
{
///
/// Color Code
///
public LogType Type;
///
/// Text
///
public object Message;
}
///
/// Thread safe coroutine command
///
public struct CoroutineCommand
{
///
/// The IEnumerator Coroutine
///
public IEnumerator Coroutine;
///
/// Called on complete
///
public Action OnComplete;
}
#endregion
///
/// Static Accessor
///
public static TaskManager Instance
{
get
{
ConfirmInit();
return _instance;
}
}
///
/// Confirms the instance is ready for use
///
public static void ConfirmInit()
{
if (_instance == null)
{
var old = FindObjectsOfType();
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();
MainThread = CurrentThread;
}
}
///
/// Scheduled the routine to run (on the main thread)
///
public static Coroutine WaitForSeconds(int seconds)
{
return Instance.StartCoroutine(Instance.WaitForSecondsInternal(seconds));
}
///
/// Scheduled the routine to run (on the main thread)
///
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);
}
///
/// Scheduled the routine to run (on the main thread)
///
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));
}
}
///
/// Scheduled the routine to run (on the main thread)
///
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);
}
}
///
/// Schedules the action to run on the main thread
///
///
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();
}
}
///
/// A thread safe logger
///
///
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 PendingCoroutineInfo = new List();
protected static readonly List PendingAdd = new List();
protected static readonly List PendingRemove = new List();
protected static readonly List PendingActions = new List();
protected static readonly List PendingLogs = new List();
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