using System; using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; namespace LeanCloud.Storage.Internal { /// /// This class represents the internal Unity dispatcher used by the LeanCloud SDK. /// /// It should be initialized once in your game, usually via AVInitializeBehavior. /// /// In certain, advanced use-cases, you may wish to use /// this to set up your dispatcher manually. /// // TODO: (richardross) Review this interface before going public. public sealed class Dispatcher { static Dispatcher() { Instance = new Dispatcher(); } private Dispatcher() { DispatcherCoroutine = CreateDispatcherCoroutine(); } public static Dispatcher Instance { get; private set; } public GameObject GameObject { get; set; } public IEnumerator DispatcherCoroutine { get; private set; } private readonly ReaderWriterLockSlim dispatchQueueLock = new ReaderWriterLockSlim(); private readonly Queue dispatchQueue = new Queue(); public void Post(Action action) { if (dispatchQueueLock.IsWriteLockHeld) { dispatchQueue.Enqueue(action); return; } dispatchQueueLock.EnterWriteLock(); try { dispatchQueue.Enqueue(action); } finally { dispatchQueueLock.ExitWriteLock(); } } private IEnumerator CreateDispatcherCoroutine() { // We must stop the first invocation here, so that we don't actually do anything until we begin looping. yield return null; while (true) { dispatchQueueLock.EnterUpgradeableReadLock(); try { // We'll only empty what's already in the dispatch queue in this iteration (so that a // nested dispatch behaves like nextTick()). int count = dispatchQueue.Count; if (count > 0) { dispatchQueueLock.EnterWriteLock(); try { while (count > 0) { try { dispatchQueue.Dequeue()(); } catch (Exception e) { // If an exception occurs, catch it and log it so that dispatches aren't broken. Debug.LogException(e); } count--; } } finally { dispatchQueueLock.ExitWriteLock(); } } } finally { dispatchQueueLock.ExitUpgradeableReadLock(); } yield return null; } } } }