Files
StoryGame_Unity/Assets/Live2D/Cubism/Framework/Tasking/CubismBuiltinAsyncTaskHandler.cs

197 lines
4.6 KiB
C#

/**
* 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
}
}