upm_guru_dof_lib/Runtime/LoomUtil.cs

219 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace DofLibrary
{
// Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
public class LoomUtil : MonoBehaviour
{
public static int maxThreads = 10;
private static int numThreads;
private static LoomUtil _instance;
public static LoomUtil Instance
{
get
{
Initialize();
return _instance;
}
}
void Awake()
{
_instance = this;
initialized = true;
}
static bool initialized;
public static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying) return;
initialized = true;
var g = new GameObject("Loom");
DontDestroyOnLoad(g);
_instance = g.AddComponent<LoomUtil>();
}
}
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<Action> _actions = new List<Action>();
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
// Runs a set of statements on the main thread
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
// Runs a set of statements on the main thread (with an optional delay).
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (_instance._delayed)
{
_instance._delayed.Add(new DelayedQueueItem { time = Time.unscaledTime + time, action = action });
}
}
else
{
lock (_instance._actions)
{
_instance._actions.Add(action);
}
}
}
// Runs a set of statements on another thread
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
public static void Reset()
{
if (_instance == null) return;
lock (_instance._delayed)
{
_instance._delayed.Clear();
_instance._currentDelayed.Clear();
}
lock (_instance._actions)
{
_instance._actions.Clear();
}
}
public bool IsThreadsMax()
{
return numThreads >= maxThreads;
}
void OnDisable()
{
if (_instance == this)
{
_instance = null;
}
}
List<Action> _currentActions = new List<Action>();
void Update()
{
if (_actions.Count > 0)
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
for (int i = 0; i < _currentActions.Count; i++)
{
try
{
_currentActions[i].Invoke();
}
catch(Exception ex)
{
LogException(ex);
}
}
if (_delayed.Count > 0)
{
lock (_delayed)
{
_currentDelayed.Clear();
for (int i = 0; i < _delayed.Count; i++)
{
var d = _delayed[i];
if (d.time <= Time.unscaledTime)
{
_currentDelayed.Add(d);
}
}
for (int i = 0; i < _currentDelayed.Count; i++)
{
var item = _currentDelayed[i];
_delayed.Remove(item);
}
}
for (int i = 0; i < _currentDelayed.Count; i++)
{
try
{
_currentDelayed[i].action.Invoke();
}
catch (Exception ex)
{
LogException(ex);
}
}
}
}
}
void LogException(Exception ex)
{
string msg = ex.Message;
string stack_trace = ex.StackTrace;
if (ex.InnerException != null)
{
msg = ex.InnerException.Message;
stack_trace = ex.InnerException.StackTrace;
}
Debug.LogError($"[Loom]InvokeMainThreadAction: {msg}\nStack Trace: {stack_trace}");
}
}
}