// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; using UnityEngine; namespace Microsoft.MixedReality.WebRTC.Unity { /// /// Base class providing some utility work queue to dispatch free-threaded actions /// to the main Unity application thread, where the handler(s) can safely access /// Unity objects. /// public class WorkQueue : MonoBehaviour { /// /// Check if the current thread is the main Unity application thread where /// it is safe to access Unity objects. /// /// /// Should be only called once the object is awake. /// public bool IsMainAppThread { get { UnityEngine.Debug.Assert(_mainAppThread != null, "This method can only be called once the object is awake."); return Thread.CurrentThread == _mainAppThread; } } /// /// Ensure the current method is running on the main Unity application thread. /// /// /// Should be only called once the object is awake. /// [Conditional("UNITY_ASSERTIONS")] public void EnsureIsMainAppThread() { UnityEngine.Debug.Assert(IsMainAppThread, "This method can only be called from the main Unity application thread."); } /// /// Invoke the specified action on the main Unity app thread. /// /// The action to execute. /// /// If this object is awake, and this method is called from the main Unity app thread, /// will be executed synchronously. Otherwise, /// will be called during the next call to this object's . /// public void InvokeOnAppThread(Action action) { if (_mainAppThread != null && IsMainAppThread) { action(); } else { _mainThreadWorkQueue.Enqueue(action); } } protected virtual void Awake() { // Awake() is always called from the main Unity app thread _mainAppThread = Thread.CurrentThread; } /// /// Implementation of MonoBehaviour.Update /// to execute from the main Unity app thread any background work enqueued from free-threaded callbacks. /// protected virtual void Update() { // Execute any pending work enqueued by background tasks while (_mainThreadWorkQueue.TryDequeue(out Action workload)) { workload(); } } /// /// Internal queue used to marshal work back to the main Unity app thread, which is the /// only thread where access to Unity objects is allowed. This is used by free-threaded /// callbacks to defer some of their work, generally a final user notification via an event. /// private readonly ConcurrentQueue _mainThreadWorkQueue = new ConcurrentQueue(); /// /// Reference to the main Unity application thread where it is safe to access Unity objects. /// private Thread _mainAppThread = null; } }