diff --git a/UnityMainThreadDispatcher.cs b/UnityMainThreadDispatcher.cs index 7a4f783..331a2cd 100644 --- a/UnityMainThreadDispatcher.cs +++ b/UnityMainThreadDispatcher.cs @@ -18,6 +18,7 @@ limitations under the License. using System.Collections; using System.Collections.Generic; using System; +using System.Threading; using System.Threading.Tasks; /// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher @@ -25,95 +26,121 @@ limitations under the License. /// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for /// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling /// -public class UnityMainThreadDispatcher : MonoBehaviour { - - private static readonly Queue _executionQueue = new Queue(); - - public void Update() { - lock(_executionQueue) { - while (_executionQueue.Count > 0) { - _executionQueue.Dequeue().Invoke(); - } - } - } - - /// - /// Locks the queue and adds the IEnumerator to the queue - /// - /// IEnumerator function that will be executed from the main thread. - public void Enqueue(IEnumerator action) { - lock (_executionQueue) { - _executionQueue.Enqueue (() => { - StartCoroutine (action); - }); - } - } - - /// - /// Locks the queue and adds the Action to the queue - /// - /// function that will be executed from the main thread. - public void Enqueue(Action action) - { - Enqueue(ActionWrapper(action)); - } - - /// - /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes - /// - /// function that will be executed from the main thread. - /// A Task that can be awaited until the action completes - public Task EnqueueAsync(Action action) - { - var tcs = new TaskCompletionSource(); - - void WrappedAction() { - try - { - action(); - tcs.TrySetResult(true); - } catch (Exception ex) - { - tcs.TrySetException(ex); - } - } - - Enqueue(ActionWrapper(WrappedAction)); - return tcs.Task; - } - - - IEnumerator ActionWrapper(Action a) - { - a(); - yield return null; - } - - - private static UnityMainThreadDispatcher _instance = null; - - public static bool Exists() { - return _instance != null; - } - - public static UnityMainThreadDispatcher Instance() { - if (!Exists ()) { - throw new Exception ("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene."); - } - return _instance; - } - - - void Awake() { - if (_instance == null) { - _instance = this; - DontDestroyOnLoad(this.gameObject); - } - } - - void OnDestroy() { - _instance = null; - } +public class UnityMainThreadDispatcher : MonoBehaviour +{ + + private static readonly Queue _executionQueue = new Queue(); + private static SemaphoreSlim _executionQueueLock = new SemaphoreSlim(1, 1); + + public void Update() + { + _executionQueueLock.Wait(); + try + { + while (_executionQueue.Count > 0) + { + _executionQueue.Dequeue().Invoke(); + } + } + finally + { + _executionQueueLock.Release(); + } + } + + /// + /// Locks the queue and adds the IEnumerator to the queue + /// + /// IEnumerator function that will be executed from the main thread. + public void Enqueue(IEnumerator action) + { + _executionQueueLock.Wait(); + try + { + _executionQueue.Enqueue(() => + { + StartCoroutine(action); + }); + } + finally + { + _executionQueueLock.Release(); + } + } + + /// + /// Locks the queue and adds the Action to the queue + /// + /// function that will be executed from the main thread. + public void Enqueue(Action action) + { + Enqueue(ActionWrapper(action)); + } + + /// + /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes + /// + /// function that will be executed from the main thread. + /// A Task that can be awaited until the action completes + public Task EnqueueAsync(Action action) + { + var tcs = new TaskCompletionSource(); + + void WrappedAction() + { + try + { + action(); + tcs.TrySetResult(true); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + } + + Enqueue(ActionWrapper(WrappedAction)); + return tcs.Task; + } + + + IEnumerator ActionWrapper(Action a) + { + a(); + yield return null; + } + + + private static UnityMainThreadDispatcher _instance = null; + + public static bool Exists() + { + return _instance != null; + } + + public static UnityMainThreadDispatcher Instance() + { + if (!Exists()) + { + throw new Exception("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene."); + } + return _instance; + } + + + void Awake() + { + if (_instance == null) + { + _instance = this; + DontDestroyOnLoad(this.gameObject); + } + } + + void OnDestroy() + { + _instance = null; + } }