You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1133 rivejä
40 KiB

  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using Cysharp.Threading.Tasks.Internal;
  3. using System;
  4. using System.Collections;
  5. using System.Runtime.CompilerServices;
  6. using System.Threading;
  7. using UnityEngine;
  8. namespace Cysharp.Threading.Tasks
  9. {
  10. public enum DelayType
  11. {
  12. /// <summary>use Time.deltaTime.</summary>
  13. DeltaTime,
  14. /// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
  15. UnscaledDeltaTime,
  16. /// <summary>use Stopwatch.GetTimestamp().</summary>
  17. Realtime
  18. }
  19. public partial struct UniTask
  20. {
  21. public static YieldAwaitable Yield()
  22. {
  23. // optimized for single continuation
  24. return new YieldAwaitable(PlayerLoopTiming.Update);
  25. }
  26. public static YieldAwaitable Yield(PlayerLoopTiming timing)
  27. {
  28. // optimized for single continuation
  29. return new YieldAwaitable(timing);
  30. }
  31. public static UniTask Yield(CancellationToken cancellationToken, bool cancelImmediately = false)
  32. {
  33. return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
  34. }
  35. public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
  36. {
  37. return new UniTask(YieldPromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
  38. }
  39. /// <summary>
  40. /// Similar as UniTask.Yield but guaranteed run on next frame.
  41. /// </summary>
  42. public static UniTask NextFrame()
  43. {
  44. return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, false, out var token), token);
  45. }
  46. /// <summary>
  47. /// Similar as UniTask.Yield but guaranteed run on next frame.
  48. /// </summary>
  49. public static UniTask NextFrame(PlayerLoopTiming timing)
  50. {
  51. return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, false, out var token), token);
  52. }
  53. /// <summary>
  54. /// Similar as UniTask.Yield but guaranteed run on next frame.
  55. /// </summary>
  56. public static UniTask NextFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
  57. {
  58. return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
  59. }
  60. /// <summary>
  61. /// Similar as UniTask.Yield but guaranteed run on next frame.
  62. /// </summary>
  63. public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
  64. {
  65. return new UniTask(NextFramePromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
  66. }
  67. #if UNITY_2023_1_OR_NEWER
  68. public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToken = default)
  69. {
  70. await Awaitable.EndOfFrameAsync(cancellationToken);
  71. }
  72. #else
  73. [Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
  74. public static YieldAwaitable WaitForEndOfFrame()
  75. {
  76. return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
  77. }
  78. [Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
  79. public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
  80. {
  81. return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken, cancelImmediately);
  82. }
  83. #endif
  84. public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner)
  85. {
  86. var source = WaitForEndOfFramePromise.Create(coroutineRunner, CancellationToken.None, false, out var token);
  87. return new UniTask(source, token);
  88. }
  89. public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately = false)
  90. {
  91. var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, cancelImmediately, out var token);
  92. return new UniTask(source, token);
  93. }
  94. /// <summary>
  95. /// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate).
  96. /// </summary>
  97. public static YieldAwaitable WaitForFixedUpdate()
  98. {
  99. // use LastFixedUpdate instead of FixedUpdate
  100. // https://github.com/Cysharp/UniTask/issues/377
  101. return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate);
  102. }
  103. /// <summary>
  104. /// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
  105. /// </summary>
  106. public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken, bool cancelImmediately = false)
  107. {
  108. return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken, cancelImmediately);
  109. }
  110. public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  111. {
  112. return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
  113. }
  114. public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  115. {
  116. return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
  117. }
  118. public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  119. {
  120. if (delayFrameCount < 0)
  121. {
  122. throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
  123. }
  124. return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, cancelImmediately, out var token), token);
  125. }
  126. public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  127. {
  128. var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
  129. return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
  130. }
  131. public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  132. {
  133. var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
  134. return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
  135. }
  136. public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  137. {
  138. var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
  139. return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
  140. }
  141. public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  142. {
  143. if (delayTimeSpan < TimeSpan.Zero)
  144. {
  145. throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
  146. }
  147. #if UNITY_EDITOR
  148. // force use Realtime.
  149. if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
  150. {
  151. delayType = DelayType.Realtime;
  152. }
  153. #endif
  154. switch (delayType)
  155. {
  156. case DelayType.UnscaledDeltaTime:
  157. {
  158. return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
  159. }
  160. case DelayType.Realtime:
  161. {
  162. return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
  163. }
  164. case DelayType.DeltaTime:
  165. default:
  166. {
  167. return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
  168. }
  169. }
  170. }
  171. sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
  172. {
  173. static TaskPool<YieldPromise> pool;
  174. YieldPromise nextNode;
  175. public ref YieldPromise NextNode => ref nextNode;
  176. static YieldPromise()
  177. {
  178. TaskPool.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
  179. }
  180. CancellationToken cancellationToken;
  181. CancellationTokenRegistration cancellationTokenRegistration;
  182. bool cancelImmediately;
  183. UniTaskCompletionSourceCore<object> core;
  184. YieldPromise()
  185. {
  186. }
  187. public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  188. {
  189. if (cancellationToken.IsCancellationRequested)
  190. {
  191. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  192. }
  193. if (!pool.TryPop(out var result))
  194. {
  195. result = new YieldPromise();
  196. }
  197. result.cancellationToken = cancellationToken;
  198. result.cancelImmediately = cancelImmediately;
  199. if (cancelImmediately && cancellationToken.CanBeCanceled)
  200. {
  201. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  202. {
  203. var promise = (YieldPromise)state;
  204. promise.core.TrySetCanceled(promise.cancellationToken);
  205. }, result);
  206. }
  207. TaskTracker.TrackActiveTask(result, 3);
  208. PlayerLoopHelper.AddAction(timing, result);
  209. token = result.core.Version;
  210. return result;
  211. }
  212. public void GetResult(short token)
  213. {
  214. try
  215. {
  216. core.GetResult(token);
  217. }
  218. finally
  219. {
  220. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  221. {
  222. TryReturn();
  223. }
  224. else
  225. {
  226. TaskTracker.RemoveTracking(this);
  227. }
  228. }
  229. }
  230. public UniTaskStatus GetStatus(short token)
  231. {
  232. return core.GetStatus(token);
  233. }
  234. public UniTaskStatus UnsafeGetStatus()
  235. {
  236. return core.UnsafeGetStatus();
  237. }
  238. public void OnCompleted(Action<object> continuation, object state, short token)
  239. {
  240. core.OnCompleted(continuation, state, token);
  241. }
  242. public bool MoveNext()
  243. {
  244. if (cancellationToken.IsCancellationRequested)
  245. {
  246. core.TrySetCanceled(cancellationToken);
  247. return false;
  248. }
  249. core.TrySetResult(null);
  250. return false;
  251. }
  252. bool TryReturn()
  253. {
  254. TaskTracker.RemoveTracking(this);
  255. core.Reset();
  256. cancellationToken = default;
  257. cancellationTokenRegistration.Dispose();
  258. cancelImmediately = default;
  259. return pool.TryPush(this);
  260. }
  261. }
  262. sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
  263. {
  264. static TaskPool<NextFramePromise> pool;
  265. NextFramePromise nextNode;
  266. public ref NextFramePromise NextNode => ref nextNode;
  267. static NextFramePromise()
  268. {
  269. TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
  270. }
  271. int frameCount;
  272. UniTaskCompletionSourceCore<AsyncUnit> core;
  273. CancellationToken cancellationToken;
  274. CancellationTokenRegistration cancellationTokenRegistration;
  275. bool cancelImmediately;
  276. NextFramePromise()
  277. {
  278. }
  279. public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  280. {
  281. if (cancellationToken.IsCancellationRequested)
  282. {
  283. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  284. }
  285. if (!pool.TryPop(out var result))
  286. {
  287. result = new NextFramePromise();
  288. }
  289. result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
  290. result.cancellationToken = cancellationToken;
  291. result.cancelImmediately = cancelImmediately;
  292. if (cancelImmediately && cancellationToken.CanBeCanceled)
  293. {
  294. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  295. {
  296. var promise = (NextFramePromise)state;
  297. promise.core.TrySetCanceled(promise.cancellationToken);
  298. }, result);
  299. }
  300. TaskTracker.TrackActiveTask(result, 3);
  301. PlayerLoopHelper.AddAction(timing, result);
  302. token = result.core.Version;
  303. return result;
  304. }
  305. public void GetResult(short token)
  306. {
  307. try
  308. {
  309. core.GetResult(token);
  310. }
  311. finally
  312. {
  313. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  314. {
  315. TryReturn();
  316. }
  317. else
  318. {
  319. TaskTracker.RemoveTracking(this);
  320. }
  321. }
  322. }
  323. public UniTaskStatus GetStatus(short token)
  324. {
  325. return core.GetStatus(token);
  326. }
  327. public UniTaskStatus UnsafeGetStatus()
  328. {
  329. return core.UnsafeGetStatus();
  330. }
  331. public void OnCompleted(Action<object> continuation, object state, short token)
  332. {
  333. core.OnCompleted(continuation, state, token);
  334. }
  335. public bool MoveNext()
  336. {
  337. if (cancellationToken.IsCancellationRequested)
  338. {
  339. core.TrySetCanceled(cancellationToken);
  340. return false;
  341. }
  342. if (frameCount == Time.frameCount)
  343. {
  344. return true;
  345. }
  346. core.TrySetResult(AsyncUnit.Default);
  347. return false;
  348. }
  349. bool TryReturn()
  350. {
  351. TaskTracker.RemoveTracking(this);
  352. core.Reset();
  353. cancellationToken = default;
  354. cancellationTokenRegistration.Dispose();
  355. return pool.TryPush(this);
  356. }
  357. }
  358. sealed class WaitForEndOfFramePromise : IUniTaskSource, ITaskPoolNode<WaitForEndOfFramePromise>, System.Collections.IEnumerator
  359. {
  360. static TaskPool<WaitForEndOfFramePromise> pool;
  361. WaitForEndOfFramePromise nextNode;
  362. public ref WaitForEndOfFramePromise NextNode => ref nextNode;
  363. static WaitForEndOfFramePromise()
  364. {
  365. TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
  366. }
  367. UniTaskCompletionSourceCore<object> core;
  368. CancellationToken cancellationToken;
  369. CancellationTokenRegistration cancellationTokenRegistration;
  370. bool cancelImmediately;
  371. WaitForEndOfFramePromise()
  372. {
  373. }
  374. public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  375. {
  376. if (cancellationToken.IsCancellationRequested)
  377. {
  378. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  379. }
  380. if (!pool.TryPop(out var result))
  381. {
  382. result = new WaitForEndOfFramePromise();
  383. }
  384. result.cancellationToken = cancellationToken;
  385. result.cancelImmediately = cancelImmediately;
  386. if (cancelImmediately && cancellationToken.CanBeCanceled)
  387. {
  388. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  389. {
  390. var promise = (WaitForEndOfFramePromise)state;
  391. promise.core.TrySetCanceled(promise.cancellationToken);
  392. }, result);
  393. }
  394. TaskTracker.TrackActiveTask(result, 3);
  395. coroutineRunner.StartCoroutine(result);
  396. token = result.core.Version;
  397. return result;
  398. }
  399. public void GetResult(short token)
  400. {
  401. try
  402. {
  403. core.GetResult(token);
  404. }
  405. finally
  406. {
  407. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  408. {
  409. TryReturn();
  410. }
  411. else
  412. {
  413. TaskTracker.RemoveTracking(this);
  414. }
  415. }
  416. }
  417. public UniTaskStatus GetStatus(short token)
  418. {
  419. return core.GetStatus(token);
  420. }
  421. public UniTaskStatus UnsafeGetStatus()
  422. {
  423. return core.UnsafeGetStatus();
  424. }
  425. public void OnCompleted(Action<object> continuation, object state, short token)
  426. {
  427. core.OnCompleted(continuation, state, token);
  428. }
  429. bool TryReturn()
  430. {
  431. TaskTracker.RemoveTracking(this);
  432. core.Reset();
  433. Reset(); // Reset Enumerator
  434. cancellationToken = default;
  435. cancellationTokenRegistration.Dispose();
  436. return pool.TryPush(this);
  437. }
  438. // Coroutine Runner implementation
  439. static readonly WaitForEndOfFrame waitForEndOfFrameYieldInstruction = new WaitForEndOfFrame();
  440. bool isFirst = true;
  441. object IEnumerator.Current => waitForEndOfFrameYieldInstruction;
  442. bool IEnumerator.MoveNext()
  443. {
  444. if (isFirst)
  445. {
  446. isFirst = false;
  447. return true; // start WaitForEndOfFrame
  448. }
  449. if (cancellationToken.IsCancellationRequested)
  450. {
  451. core.TrySetCanceled(cancellationToken);
  452. return false;
  453. }
  454. core.TrySetResult(null);
  455. return false;
  456. }
  457. public void Reset()
  458. {
  459. isFirst = true;
  460. }
  461. }
  462. sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
  463. {
  464. static TaskPool<DelayFramePromise> pool;
  465. DelayFramePromise nextNode;
  466. public ref DelayFramePromise NextNode => ref nextNode;
  467. static DelayFramePromise()
  468. {
  469. TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
  470. }
  471. int initialFrame;
  472. int delayFrameCount;
  473. CancellationToken cancellationToken;
  474. CancellationTokenRegistration cancellationTokenRegistration;
  475. bool cancelImmediately;
  476. int currentFrameCount;
  477. UniTaskCompletionSourceCore<AsyncUnit> core;
  478. DelayFramePromise()
  479. {
  480. }
  481. public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  482. {
  483. if (cancellationToken.IsCancellationRequested)
  484. {
  485. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  486. }
  487. if (!pool.TryPop(out var result))
  488. {
  489. result = new DelayFramePromise();
  490. }
  491. result.delayFrameCount = delayFrameCount;
  492. result.cancellationToken = cancellationToken;
  493. result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
  494. result.cancelImmediately = cancelImmediately;
  495. if (cancelImmediately && cancellationToken.CanBeCanceled)
  496. {
  497. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  498. {
  499. var promise = (DelayFramePromise)state;
  500. promise.core.TrySetCanceled(promise.cancellationToken);
  501. }, result);
  502. }
  503. TaskTracker.TrackActiveTask(result, 3);
  504. PlayerLoopHelper.AddAction(timing, result);
  505. token = result.core.Version;
  506. return result;
  507. }
  508. public void GetResult(short token)
  509. {
  510. try
  511. {
  512. core.GetResult(token);
  513. }
  514. finally
  515. {
  516. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  517. {
  518. TryReturn();
  519. }
  520. else
  521. {
  522. TaskTracker.RemoveTracking(this);
  523. }
  524. }
  525. }
  526. public UniTaskStatus GetStatus(short token)
  527. {
  528. return core.GetStatus(token);
  529. }
  530. public UniTaskStatus UnsafeGetStatus()
  531. {
  532. return core.UnsafeGetStatus();
  533. }
  534. public void OnCompleted(Action<object> continuation, object state, short token)
  535. {
  536. core.OnCompleted(continuation, state, token);
  537. }
  538. public bool MoveNext()
  539. {
  540. if (cancellationToken.IsCancellationRequested)
  541. {
  542. core.TrySetCanceled(cancellationToken);
  543. return false;
  544. }
  545. if (currentFrameCount == 0)
  546. {
  547. if (delayFrameCount == 0) // same as Yield
  548. {
  549. core.TrySetResult(AsyncUnit.Default);
  550. return false;
  551. }
  552. // skip in initial frame.
  553. if (initialFrame == Time.frameCount)
  554. {
  555. #if UNITY_EDITOR
  556. // force use Realtime.
  557. if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
  558. {
  559. //goto ++currentFrameCount
  560. }
  561. else
  562. {
  563. return true;
  564. }
  565. #else
  566. return true;
  567. #endif
  568. }
  569. }
  570. if (++currentFrameCount >= delayFrameCount)
  571. {
  572. core.TrySetResult(AsyncUnit.Default);
  573. return false;
  574. }
  575. return true;
  576. }
  577. bool TryReturn()
  578. {
  579. TaskTracker.RemoveTracking(this);
  580. core.Reset();
  581. currentFrameCount = default;
  582. delayFrameCount = default;
  583. cancellationToken = default;
  584. cancellationTokenRegistration.Dispose();
  585. cancelImmediately = default;
  586. return pool.TryPush(this);
  587. }
  588. }
  589. sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
  590. {
  591. static TaskPool<DelayPromise> pool;
  592. DelayPromise nextNode;
  593. public ref DelayPromise NextNode => ref nextNode;
  594. static DelayPromise()
  595. {
  596. TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
  597. }
  598. int initialFrame;
  599. float delayTimeSpan;
  600. float elapsed;
  601. CancellationToken cancellationToken;
  602. CancellationTokenRegistration cancellationTokenRegistration;
  603. bool cancelImmediately;
  604. UniTaskCompletionSourceCore<object> core;
  605. DelayPromise()
  606. {
  607. }
  608. public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  609. {
  610. if (cancellationToken.IsCancellationRequested)
  611. {
  612. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  613. }
  614. if (!pool.TryPop(out var result))
  615. {
  616. result = new DelayPromise();
  617. }
  618. result.elapsed = 0.0f;
  619. result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
  620. result.cancellationToken = cancellationToken;
  621. result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
  622. result.cancelImmediately = cancelImmediately;
  623. if (cancelImmediately && cancellationToken.CanBeCanceled)
  624. {
  625. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  626. {
  627. var promise = (DelayPromise)state;
  628. promise.core.TrySetCanceled(promise.cancellationToken);
  629. }, result);
  630. }
  631. TaskTracker.TrackActiveTask(result, 3);
  632. PlayerLoopHelper.AddAction(timing, result);
  633. token = result.core.Version;
  634. return result;
  635. }
  636. public void GetResult(short token)
  637. {
  638. try
  639. {
  640. core.GetResult(token);
  641. }
  642. finally
  643. {
  644. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  645. {
  646. TryReturn();
  647. }
  648. else
  649. {
  650. TaskTracker.RemoveTracking(this);
  651. }
  652. }
  653. }
  654. public UniTaskStatus GetStatus(short token)
  655. {
  656. return core.GetStatus(token);
  657. }
  658. public UniTaskStatus UnsafeGetStatus()
  659. {
  660. return core.UnsafeGetStatus();
  661. }
  662. public void OnCompleted(Action<object> continuation, object state, short token)
  663. {
  664. core.OnCompleted(continuation, state, token);
  665. }
  666. public bool MoveNext()
  667. {
  668. if (cancellationToken.IsCancellationRequested)
  669. {
  670. core.TrySetCanceled(cancellationToken);
  671. return false;
  672. }
  673. if (elapsed == 0.0f)
  674. {
  675. if (initialFrame == Time.frameCount)
  676. {
  677. return true;
  678. }
  679. }
  680. elapsed += Time.deltaTime;
  681. if (elapsed >= delayTimeSpan)
  682. {
  683. core.TrySetResult(null);
  684. return false;
  685. }
  686. return true;
  687. }
  688. bool TryReturn()
  689. {
  690. TaskTracker.RemoveTracking(this);
  691. core.Reset();
  692. delayTimeSpan = default;
  693. elapsed = default;
  694. cancellationToken = default;
  695. cancellationTokenRegistration.Dispose();
  696. cancelImmediately = default;
  697. return pool.TryPush(this);
  698. }
  699. }
  700. sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
  701. {
  702. static TaskPool<DelayIgnoreTimeScalePromise> pool;
  703. DelayIgnoreTimeScalePromise nextNode;
  704. public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
  705. static DelayIgnoreTimeScalePromise()
  706. {
  707. TaskPool.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
  708. }
  709. float delayFrameTimeSpan;
  710. float elapsed;
  711. int initialFrame;
  712. CancellationToken cancellationToken;
  713. CancellationTokenRegistration cancellationTokenRegistration;
  714. bool cancelImmediately;
  715. UniTaskCompletionSourceCore<object> core;
  716. DelayIgnoreTimeScalePromise()
  717. {
  718. }
  719. public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  720. {
  721. if (cancellationToken.IsCancellationRequested)
  722. {
  723. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  724. }
  725. if (!pool.TryPop(out var result))
  726. {
  727. result = new DelayIgnoreTimeScalePromise();
  728. }
  729. result.elapsed = 0.0f;
  730. result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
  731. result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
  732. result.cancellationToken = cancellationToken;
  733. result.cancelImmediately = cancelImmediately;
  734. if (cancelImmediately && cancellationToken.CanBeCanceled)
  735. {
  736. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  737. {
  738. var promise = (DelayIgnoreTimeScalePromise)state;
  739. promise.core.TrySetCanceled(promise.cancellationToken);
  740. }, result);
  741. }
  742. TaskTracker.TrackActiveTask(result, 3);
  743. PlayerLoopHelper.AddAction(timing, result);
  744. token = result.core.Version;
  745. return result;
  746. }
  747. public void GetResult(short token)
  748. {
  749. try
  750. {
  751. core.GetResult(token);
  752. }
  753. finally
  754. {
  755. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  756. {
  757. TryReturn();
  758. }
  759. else
  760. {
  761. TaskTracker.RemoveTracking(this);
  762. }
  763. }
  764. }
  765. public UniTaskStatus GetStatus(short token)
  766. {
  767. return core.GetStatus(token);
  768. }
  769. public UniTaskStatus UnsafeGetStatus()
  770. {
  771. return core.UnsafeGetStatus();
  772. }
  773. public void OnCompleted(Action<object> continuation, object state, short token)
  774. {
  775. core.OnCompleted(continuation, state, token);
  776. }
  777. public bool MoveNext()
  778. {
  779. if (cancellationToken.IsCancellationRequested)
  780. {
  781. core.TrySetCanceled(cancellationToken);
  782. return false;
  783. }
  784. if (elapsed == 0.0f)
  785. {
  786. if (initialFrame == Time.frameCount)
  787. {
  788. return true;
  789. }
  790. }
  791. elapsed += Time.unscaledDeltaTime;
  792. if (elapsed >= delayFrameTimeSpan)
  793. {
  794. core.TrySetResult(null);
  795. return false;
  796. }
  797. return true;
  798. }
  799. bool TryReturn()
  800. {
  801. TaskTracker.RemoveTracking(this);
  802. core.Reset();
  803. delayFrameTimeSpan = default;
  804. elapsed = default;
  805. cancellationToken = default;
  806. cancellationTokenRegistration.Dispose();
  807. cancelImmediately = default;
  808. return pool.TryPush(this);
  809. }
  810. }
  811. sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
  812. {
  813. static TaskPool<DelayRealtimePromise> pool;
  814. DelayRealtimePromise nextNode;
  815. public ref DelayRealtimePromise NextNode => ref nextNode;
  816. static DelayRealtimePromise()
  817. {
  818. TaskPool.RegisterSizeGetter(typeof(DelayRealtimePromise), () => pool.Size);
  819. }
  820. long delayTimeSpanTicks;
  821. ValueStopwatch stopwatch;
  822. CancellationToken cancellationToken;
  823. CancellationTokenRegistration cancellationTokenRegistration;
  824. bool cancelImmediately;
  825. UniTaskCompletionSourceCore<AsyncUnit> core;
  826. DelayRealtimePromise()
  827. {
  828. }
  829. public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  830. {
  831. if (cancellationToken.IsCancellationRequested)
  832. {
  833. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  834. }
  835. if (!pool.TryPop(out var result))
  836. {
  837. result = new DelayRealtimePromise();
  838. }
  839. result.stopwatch = ValueStopwatch.StartNew();
  840. result.delayTimeSpanTicks = delayTimeSpan.Ticks;
  841. result.cancellationToken = cancellationToken;
  842. result.cancelImmediately = cancelImmediately;
  843. if (cancelImmediately && cancellationToken.CanBeCanceled)
  844. {
  845. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  846. {
  847. var promise = (DelayRealtimePromise)state;
  848. promise.core.TrySetCanceled(promise.cancellationToken);
  849. }, result);
  850. }
  851. TaskTracker.TrackActiveTask(result, 3);
  852. PlayerLoopHelper.AddAction(timing, result);
  853. token = result.core.Version;
  854. return result;
  855. }
  856. public void GetResult(short token)
  857. {
  858. try
  859. {
  860. core.GetResult(token);
  861. }
  862. finally
  863. {
  864. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  865. {
  866. TryReturn();
  867. }
  868. else
  869. {
  870. TaskTracker.RemoveTracking(this);
  871. }
  872. }
  873. }
  874. public UniTaskStatus GetStatus(short token)
  875. {
  876. return core.GetStatus(token);
  877. }
  878. public UniTaskStatus UnsafeGetStatus()
  879. {
  880. return core.UnsafeGetStatus();
  881. }
  882. public void OnCompleted(Action<object> continuation, object state, short token)
  883. {
  884. core.OnCompleted(continuation, state, token);
  885. }
  886. public bool MoveNext()
  887. {
  888. if (cancellationToken.IsCancellationRequested)
  889. {
  890. core.TrySetCanceled(cancellationToken);
  891. return false;
  892. }
  893. if (stopwatch.IsInvalid)
  894. {
  895. core.TrySetResult(AsyncUnit.Default);
  896. return false;
  897. }
  898. if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
  899. {
  900. core.TrySetResult(AsyncUnit.Default);
  901. return false;
  902. }
  903. return true;
  904. }
  905. bool TryReturn()
  906. {
  907. TaskTracker.RemoveTracking(this);
  908. core.Reset();
  909. stopwatch = default;
  910. cancellationToken = default;
  911. cancellationTokenRegistration.Dispose();
  912. cancelImmediately = default;
  913. return pool.TryPush(this);
  914. }
  915. }
  916. }
  917. public readonly struct YieldAwaitable
  918. {
  919. readonly PlayerLoopTiming timing;
  920. public YieldAwaitable(PlayerLoopTiming timing)
  921. {
  922. this.timing = timing;
  923. }
  924. public Awaiter GetAwaiter()
  925. {
  926. return new Awaiter(timing);
  927. }
  928. public UniTask ToUniTask()
  929. {
  930. return UniTask.Yield(timing, CancellationToken.None);
  931. }
  932. public readonly struct Awaiter : ICriticalNotifyCompletion
  933. {
  934. readonly PlayerLoopTiming timing;
  935. public Awaiter(PlayerLoopTiming timing)
  936. {
  937. this.timing = timing;
  938. }
  939. public bool IsCompleted => false;
  940. public void GetResult() { }
  941. public void OnCompleted(Action continuation)
  942. {
  943. PlayerLoopHelper.AddContinuation(timing, continuation);
  944. }
  945. public void UnsafeOnCompleted(Action continuation)
  946. {
  947. PlayerLoopHelper.AddContinuation(timing, continuation);
  948. }
  949. }
  950. }
  951. }