ContinuationQueue.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Threading;
  4. namespace Cysharp.Threading.Tasks.Internal
  5. {
  6. internal sealed class ContinuationQueue
  7. {
  8. const int MaxArrayLength = 0X7FEFFFFF;
  9. const int InitialSize = 16;
  10. readonly PlayerLoopTiming timing;
  11. SpinLock gate = new SpinLock(false);
  12. bool dequing = false;
  13. int actionListCount = 0;
  14. Action[] actionList = new Action[InitialSize];
  15. int waitingListCount = 0;
  16. Action[] waitingList = new Action[InitialSize];
  17. public ContinuationQueue(PlayerLoopTiming timing)
  18. {
  19. this.timing = timing;
  20. }
  21. public void Enqueue(Action continuation)
  22. {
  23. bool lockTaken = false;
  24. try
  25. {
  26. gate.Enter(ref lockTaken);
  27. if (dequing)
  28. {
  29. // Ensure Capacity
  30. if (waitingList.Length == waitingListCount)
  31. {
  32. var newLength = waitingListCount * 2;
  33. if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
  34. var newArray = new Action[newLength];
  35. Array.Copy(waitingList, newArray, waitingListCount);
  36. waitingList = newArray;
  37. }
  38. waitingList[waitingListCount] = continuation;
  39. waitingListCount++;
  40. }
  41. else
  42. {
  43. // Ensure Capacity
  44. if (actionList.Length == actionListCount)
  45. {
  46. var newLength = actionListCount * 2;
  47. if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
  48. var newArray = new Action[newLength];
  49. Array.Copy(actionList, newArray, actionListCount);
  50. actionList = newArray;
  51. }
  52. actionList[actionListCount] = continuation;
  53. actionListCount++;
  54. }
  55. }
  56. finally
  57. {
  58. if (lockTaken) gate.Exit(false);
  59. }
  60. }
  61. public int Clear()
  62. {
  63. var rest = actionListCount + waitingListCount;
  64. actionListCount = 0;
  65. actionList = new Action[InitialSize];
  66. waitingListCount = 0;
  67. waitingList = new Action[InitialSize];
  68. return rest;
  69. }
  70. // delegate entrypoint.
  71. public void Run()
  72. {
  73. // for debugging, create named stacktrace.
  74. #if DEBUG
  75. switch (timing)
  76. {
  77. case PlayerLoopTiming.Initialization:
  78. Initialization();
  79. break;
  80. case PlayerLoopTiming.LastInitialization:
  81. LastInitialization();
  82. break;
  83. case PlayerLoopTiming.EarlyUpdate:
  84. EarlyUpdate();
  85. break;
  86. case PlayerLoopTiming.LastEarlyUpdate:
  87. LastEarlyUpdate();
  88. break;
  89. case PlayerLoopTiming.FixedUpdate:
  90. FixedUpdate();
  91. break;
  92. case PlayerLoopTiming.LastFixedUpdate:
  93. LastFixedUpdate();
  94. break;
  95. case PlayerLoopTiming.PreUpdate:
  96. PreUpdate();
  97. break;
  98. case PlayerLoopTiming.LastPreUpdate:
  99. LastPreUpdate();
  100. break;
  101. case PlayerLoopTiming.Update:
  102. Update();
  103. break;
  104. case PlayerLoopTiming.LastUpdate:
  105. LastUpdate();
  106. break;
  107. case PlayerLoopTiming.PreLateUpdate:
  108. PreLateUpdate();
  109. break;
  110. case PlayerLoopTiming.LastPreLateUpdate:
  111. LastPreLateUpdate();
  112. break;
  113. case PlayerLoopTiming.PostLateUpdate:
  114. PostLateUpdate();
  115. break;
  116. case PlayerLoopTiming.LastPostLateUpdate:
  117. LastPostLateUpdate();
  118. break;
  119. #if UNITY_2020_2_OR_NEWER
  120. case PlayerLoopTiming.TimeUpdate:
  121. TimeUpdate();
  122. break;
  123. case PlayerLoopTiming.LastTimeUpdate:
  124. LastTimeUpdate();
  125. break;
  126. #endif
  127. default:
  128. break;
  129. }
  130. #else
  131. RunCore();
  132. #endif
  133. }
  134. void Initialization() => RunCore();
  135. void LastInitialization() => RunCore();
  136. void EarlyUpdate() => RunCore();
  137. void LastEarlyUpdate() => RunCore();
  138. void FixedUpdate() => RunCore();
  139. void LastFixedUpdate() => RunCore();
  140. void PreUpdate() => RunCore();
  141. void LastPreUpdate() => RunCore();
  142. void Update() => RunCore();
  143. void LastUpdate() => RunCore();
  144. void PreLateUpdate() => RunCore();
  145. void LastPreLateUpdate() => RunCore();
  146. void PostLateUpdate() => RunCore();
  147. void LastPostLateUpdate() => RunCore();
  148. #if UNITY_2020_2_OR_NEWER
  149. void TimeUpdate() => RunCore();
  150. void LastTimeUpdate() => RunCore();
  151. #endif
  152. [System.Diagnostics.DebuggerHidden]
  153. void RunCore()
  154. {
  155. {
  156. bool lockTaken = false;
  157. try
  158. {
  159. gate.Enter(ref lockTaken);
  160. if (actionListCount == 0) return;
  161. dequing = true;
  162. }
  163. finally
  164. {
  165. if (lockTaken) gate.Exit(false);
  166. }
  167. }
  168. for (int i = 0; i < actionListCount; i++)
  169. {
  170. var action = actionList[i];
  171. actionList[i] = null;
  172. try
  173. {
  174. action();
  175. }
  176. catch (Exception ex)
  177. {
  178. UnityEngine.Debug.LogException(ex);
  179. }
  180. }
  181. {
  182. bool lockTaken = false;
  183. try
  184. {
  185. gate.Enter(ref lockTaken);
  186. dequing = false;
  187. var swapTempActionList = actionList;
  188. actionListCount = waitingListCount;
  189. actionList = waitingList;
  190. waitingListCount = 0;
  191. waitingList = swapTempActionList;
  192. }
  193. finally
  194. {
  195. if (lockTaken) gate.Exit(false);
  196. }
  197. }
  198. }
  199. }
  200. }