CubismBuiltinAsyncTaskHandler.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /**
  2. * Copyright(c) Live2D Inc. All rights reserved.
  3. *
  4. * Use of this source code is governed by the Live2D Open Software license
  5. * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
  6. */
  7. using Live2D.Cubism.Core;
  8. using System.Collections.Generic;
  9. using System.Threading;
  10. using UnityEngine;
  11. namespace Live2D.Cubism.Framework.Tasking
  12. {
  13. /// <summary>
  14. /// Built-in task handler, works async.
  15. /// </summary>
  16. public static class CubismBuiltinAsyncTaskHandler
  17. {
  18. #region Workers
  19. /// <summary>
  20. /// <see cref="ICubismTask"/>s waiting for execution.
  21. /// </summary>
  22. private static Queue<ICubismTask> Tasks { get; set; }
  23. /// <summary>
  24. /// Background worker threads.
  25. /// </summary>
  26. private static Thread Worker { get; set; }
  27. /// <summary>
  28. /// Lock for syncing access to <see cref="Tasks"/> and <see cref="CallItADay"/>.
  29. /// </summary>
  30. private static object Lock { get; set; }
  31. /// <summary>
  32. /// Signal for waking up workers.
  33. /// </summary>
  34. private static ManualResetEvent Signal { get; set; }
  35. /// <summary>
  36. /// <see cref="CallItADay"/> backing field. ALWAYS ACCESS THROUGH PROPERTY!
  37. /// </summary>
  38. private static bool _callItADay;
  39. /// <summary>
  40. /// True if workers should exit.
  41. /// </summary>
  42. private static bool CallItADay
  43. {
  44. get
  45. {
  46. lock (Lock)
  47. {
  48. return _callItADay;
  49. }
  50. }
  51. set
  52. {
  53. lock (Lock)
  54. {
  55. _callItADay = value;
  56. }
  57. }
  58. }
  59. /// <summary>
  60. /// Initializes async task handling.
  61. /// </summary>
  62. public static void Activate()
  63. {
  64. // Check if it is already set.
  65. if (CubismTaskQueue.OnTask != null && CubismTaskQueue.OnTask != EnqueueTask)
  66. {
  67. Debug.LogWarning("\"CubismTaskQueue.OnTask\" already set.");
  68. return;
  69. }
  70. // Initialize fields.
  71. Tasks = new Queue<ICubismTask>();
  72. Worker = new Thread(Work);
  73. Lock = new object();
  74. Signal = new ManualResetEvent(false);
  75. CallItADay = false;
  76. // Become handler.
  77. CubismTaskQueue.OnTask = EnqueueTask;
  78. // Start worker.
  79. Worker.Start();
  80. }
  81. /// <summary>
  82. /// Cleanup workers.
  83. /// </summary>
  84. public static void Deactivate()
  85. {
  86. // Return early if self isn' handler.
  87. if (CubismTaskQueue.OnTask != EnqueueTask)
  88. {
  89. return;
  90. }
  91. // Unbecome handler.
  92. CubismTaskQueue.OnTask = null;
  93. // Stop worker.
  94. CallItADay = true;
  95. if (Worker != null)
  96. {
  97. Signal.Set();
  98. Worker.Join();
  99. }
  100. // Reset fields
  101. Tasks = null;
  102. Worker = null;
  103. Lock = null;
  104. Signal = null;
  105. }
  106. /// <summary>
  107. /// Enqueues a new task.
  108. /// </summary>
  109. /// <param name="task">Task to enqueue.</param>
  110. private static void EnqueueTask(ICubismTask task)
  111. {
  112. lock (Lock)
  113. {
  114. Tasks.Enqueue(task);
  115. Signal.Set();
  116. }
  117. }
  118. /// <summary>
  119. /// Dequeues a task.
  120. /// </summary>
  121. /// <returns>A valid task on success; <see langword="null"/> otherwise.</returns>
  122. private static ICubismTask DequeueTask()
  123. {
  124. lock (Lock)
  125. {
  126. return (Tasks.Count > 0)
  127. ? Tasks.Dequeue()
  128. : null;
  129. }
  130. }
  131. /// <summary>
  132. /// Entry point for workers.
  133. /// </summary>
  134. private static void Work()
  135. {
  136. while (!CallItADay)
  137. {
  138. // Try to dequeue a task.
  139. var task = DequeueTask();
  140. // Execute task if available.
  141. if (task != null)
  142. {
  143. task.Execute();
  144. }
  145. // Wait for a task to become available.
  146. else
  147. {
  148. Signal.WaitOne();
  149. Signal.Reset();
  150. }
  151. }
  152. }
  153. #endregion
  154. }
  155. }