| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 | /** * 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{    /// <summary>    /// Built-in task handler, works async.    /// </summary>    public static class CubismBuiltinAsyncTaskHandler    {        #region Workers        /// <summary>        /// <see cref="ICubismTask"/>s waiting for execution.        /// </summary>        private static Queue<ICubismTask> Tasks { get; set; }        /// <summary>        /// Background worker threads.        /// </summary>        private static Thread Worker { get; set; }        /// <summary>        /// Lock for syncing access to <see cref="Tasks"/> and <see cref="CallItADay"/>.        /// </summary>        private static object Lock { get; set; }        /// <summary>        /// Signal for waking up workers.        /// </summary>        private static ManualResetEvent Signal { get; set; }        /// <summary>        /// <see cref="CallItADay"/> backing field. ALWAYS ACCESS THROUGH PROPERTY!        /// </summary>        private static bool _callItADay;        /// <summary>        /// True if workers should exit.        /// </summary>        private static bool CallItADay        {            get            {                lock (Lock)                {                    return _callItADay;                }            }            set            {                lock (Lock)                {                    _callItADay = value;                }            }        }        /// <summary>        /// Initializes async task handling.        /// </summary>        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<ICubismTask>();            Worker = new Thread(Work);            Lock = new object();            Signal = new ManualResetEvent(false);            CallItADay = false;            // Become handler.            CubismTaskQueue.OnTask = EnqueueTask;            // Start worker.            Worker.Start();        }        /// <summary>        /// Cleanup workers.        /// </summary>        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;        }        /// <summary>        /// Enqueues a new task.        /// </summary>        /// <param name="task">Task to enqueue.</param>        private static void EnqueueTask(ICubismTask task)        {            lock (Lock)            {                Tasks.Enqueue(task);                Signal.Set();            }        }        /// <summary>        /// Dequeues a task.        /// </summary>        /// <returns>A valid task on success; <see langword="null"/> otherwise.</returns>        private static ICubismTask DequeueTask()        {            lock (Lock)            {                return (Tasks.Count > 0)                    ? Tasks.Dequeue()                    : null;            }        }        /// <summary>        /// Entry point for workers.        /// </summary>        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    }}
 |