219 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Threading;
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
namespace Guru
 | 
						|
{
 | 
						|
    // 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 Loom : MonoBehaviour
 | 
						|
    {
 | 
						|
        public static int maxThreads = 10;
 | 
						|
 | 
						|
        private static int numThreads;
 | 
						|
        private static Loom _instance;
 | 
						|
 | 
						|
        public static Loom 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<Loom>();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        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}");
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |