/** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ using Live2D.Cubism.Core; using System.Collections.Generic; using System.Threading; using UnityEngine; namespace Live2D.Cubism.Framework.Tasking { /// /// Built-in task handler, works async. /// public static class CubismBuiltinAsyncTaskHandler { #region Workers /// /// s waiting for execution. /// private static Queue Tasks { get; set; } /// /// Background worker threads. /// private static Thread Worker { get; set; } /// /// Lock for syncing access to and . /// private static object Lock { get; set; } /// /// Signal for waking up workers. /// private static ManualResetEvent Signal { get; set; } /// /// backing field. ALWAYS ACCESS THROUGH PROPERTY! /// private static bool _callItADay; /// /// True if workers should exit. /// private static bool CallItADay { get { lock (Lock) { return _callItADay; } } set { lock (Lock) { _callItADay = value; } } } /// /// Initializes async task handling. /// public static void Activate() { // Check if it is already set. if (CubismTaskQueue.OnTask != null && CubismTaskQueue.OnTask != EnqueueTask) { Debug.LogWarning("\"CubismTaskQueue.OnTask\" already set."); return; } // Initialize fields. Tasks = new Queue(); Worker = new Thread(Work); Lock = new object(); Signal = new ManualResetEvent(false); CallItADay = false; // Become handler. CubismTaskQueue.OnTask = EnqueueTask; // Start worker. Worker.Start(); } /// /// Cleanup workers. /// public static void Deactivate() { // Return early if self isn' handler. if (CubismTaskQueue.OnTask != EnqueueTask) { return; } // Unbecome handler. CubismTaskQueue.OnTask = null; // Stop worker. CallItADay = true; if (Worker != null) { Signal.Set(); Worker.Join(); } // Reset fields Tasks = null; Worker = null; Lock = null; Signal = null; } /// /// Enqueues a new task. /// /// Task to enqueue. private static void EnqueueTask(ICubismTask task) { lock (Lock) { Tasks.Enqueue(task); Signal.Set(); } } /// /// Dequeues a task. /// /// A valid task on success; otherwise. private static ICubismTask DequeueTask() { lock (Lock) { return (Tasks.Count > 0) ? Tasks.Dequeue() : null; } } /// /// Entry point for workers. /// private static void Work() { while (!CallItADay) { // Try to dequeue a task. var task = DequeueTask(); // Execute task if available. if (task != null) { task.Execute(); } // Wait for a task to become available. else { Signal.WaitOne(); Signal.Reset(); } } } #endregion } }