219 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			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}"); | |||
|  |         } | |||
|  |     } | |||
|  | } |