UniTask.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. #pragma warning disable CS0436
  3. using Cysharp.Threading.Tasks.CompilerServices;
  4. using System;
  5. using System.Diagnostics;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.ExceptionServices;
  8. using System.Runtime.InteropServices;
  9. namespace Cysharp.Threading.Tasks
  10. {
  11. internal static class AwaiterActions
  12. {
  13. internal static readonly Action<object> InvokeContinuationDelegate = Continuation;
  14. [DebuggerHidden]
  15. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  16. static void Continuation(object state)
  17. {
  18. ((Action)state).Invoke();
  19. }
  20. }
  21. /// <summary>
  22. /// Lightweight unity specified task-like object.
  23. /// </summary>
  24. [AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))]
  25. [StructLayout(LayoutKind.Auto)]
  26. public readonly partial struct UniTask
  27. {
  28. readonly IUniTaskSource source;
  29. readonly short token;
  30. [DebuggerHidden]
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. public UniTask(IUniTaskSource source, short token)
  33. {
  34. this.source = source;
  35. this.token = token;
  36. }
  37. public UniTaskStatus Status
  38. {
  39. [DebuggerHidden]
  40. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  41. get
  42. {
  43. if (source == null) return UniTaskStatus.Succeeded;
  44. return source.GetStatus(token);
  45. }
  46. }
  47. [DebuggerHidden]
  48. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  49. public Awaiter GetAwaiter()
  50. {
  51. return new Awaiter(this);
  52. }
  53. /// <summary>
  54. /// returns (bool IsCanceled) instead of throws OperationCanceledException.
  55. /// </summary>
  56. public UniTask<bool> SuppressCancellationThrow()
  57. {
  58. var status = Status;
  59. if (status == UniTaskStatus.Succeeded) return CompletedTasks.False;
  60. if (status == UniTaskStatus.Canceled) return CompletedTasks.True;
  61. return new UniTask<bool>(new IsCanceledSource(source), token);
  62. }
  63. #if !UNITY_2018_3_OR_NEWER
  64. public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self)
  65. {
  66. if (self.source == null)
  67. {
  68. return default;
  69. }
  70. #if NETSTANDARD2_0
  71. return self.AsValueTask();
  72. #else
  73. return new System.Threading.Tasks.ValueTask(self.source, self.token);
  74. #endif
  75. }
  76. #endif
  77. public override string ToString()
  78. {
  79. if (source == null) return "()";
  80. return "(" + source.UnsafeGetStatus() + ")";
  81. }
  82. /// <summary>
  83. /// Memoizing inner IValueTaskSource. The result UniTask can await multiple.
  84. /// </summary>
  85. public UniTask Preserve()
  86. {
  87. if (source == null)
  88. {
  89. return this;
  90. }
  91. else
  92. {
  93. return new UniTask(new MemoizeSource(source), token);
  94. }
  95. }
  96. public UniTask<AsyncUnit> AsAsyncUnitUniTask()
  97. {
  98. if (this.source == null) return CompletedTasks.AsyncUnit;
  99. var status = this.source.GetStatus(this.token);
  100. if (status.IsCompletedSuccessfully())
  101. {
  102. this.source.GetResult(this.token);
  103. return CompletedTasks.AsyncUnit;
  104. }
  105. else if(this.source is IUniTaskSource<AsyncUnit> asyncUnitSource)
  106. {
  107. return new UniTask<AsyncUnit>(asyncUnitSource, this.token);
  108. }
  109. return new UniTask<AsyncUnit>(new AsyncUnitSource(this.source), this.token);
  110. }
  111. sealed class AsyncUnitSource : IUniTaskSource<AsyncUnit>
  112. {
  113. readonly IUniTaskSource source;
  114. public AsyncUnitSource(IUniTaskSource source)
  115. {
  116. this.source = source;
  117. }
  118. public AsyncUnit GetResult(short token)
  119. {
  120. source.GetResult(token);
  121. return AsyncUnit.Default;
  122. }
  123. public UniTaskStatus GetStatus(short token)
  124. {
  125. return source.GetStatus(token);
  126. }
  127. public void OnCompleted(Action<object> continuation, object state, short token)
  128. {
  129. source.OnCompleted(continuation, state, token);
  130. }
  131. public UniTaskStatus UnsafeGetStatus()
  132. {
  133. return source.UnsafeGetStatus();
  134. }
  135. void IUniTaskSource.GetResult(short token)
  136. {
  137. GetResult(token);
  138. }
  139. }
  140. sealed class IsCanceledSource : IUniTaskSource<bool>
  141. {
  142. readonly IUniTaskSource source;
  143. public IsCanceledSource(IUniTaskSource source)
  144. {
  145. this.source = source;
  146. }
  147. public bool GetResult(short token)
  148. {
  149. if (source.GetStatus(token) == UniTaskStatus.Canceled)
  150. {
  151. return true;
  152. }
  153. source.GetResult(token);
  154. return false;
  155. }
  156. void IUniTaskSource.GetResult(short token)
  157. {
  158. GetResult(token);
  159. }
  160. public UniTaskStatus GetStatus(short token)
  161. {
  162. return source.GetStatus(token);
  163. }
  164. public UniTaskStatus UnsafeGetStatus()
  165. {
  166. return source.UnsafeGetStatus();
  167. }
  168. public void OnCompleted(Action<object> continuation, object state, short token)
  169. {
  170. source.OnCompleted(continuation, state, token);
  171. }
  172. }
  173. sealed class MemoizeSource : IUniTaskSource
  174. {
  175. IUniTaskSource source;
  176. ExceptionDispatchInfo exception;
  177. UniTaskStatus status;
  178. public MemoizeSource(IUniTaskSource source)
  179. {
  180. this.source = source;
  181. }
  182. public void GetResult(short token)
  183. {
  184. if (source == null)
  185. {
  186. if (exception != null)
  187. {
  188. exception.Throw();
  189. }
  190. }
  191. else
  192. {
  193. try
  194. {
  195. source.GetResult(token);
  196. status = UniTaskStatus.Succeeded;
  197. }
  198. catch (Exception ex)
  199. {
  200. exception = ExceptionDispatchInfo.Capture(ex);
  201. if (ex is OperationCanceledException)
  202. {
  203. status = UniTaskStatus.Canceled;
  204. }
  205. else
  206. {
  207. status = UniTaskStatus.Faulted;
  208. }
  209. throw;
  210. }
  211. finally
  212. {
  213. source = null;
  214. }
  215. }
  216. }
  217. public UniTaskStatus GetStatus(short token)
  218. {
  219. if (source == null)
  220. {
  221. return status;
  222. }
  223. return source.GetStatus(token);
  224. }
  225. public void OnCompleted(Action<object> continuation, object state, short token)
  226. {
  227. if (source == null)
  228. {
  229. continuation(state);
  230. }
  231. else
  232. {
  233. source.OnCompleted(continuation, state, token);
  234. }
  235. }
  236. public UniTaskStatus UnsafeGetStatus()
  237. {
  238. if (source == null)
  239. {
  240. return status;
  241. }
  242. return source.UnsafeGetStatus();
  243. }
  244. }
  245. public readonly struct Awaiter : ICriticalNotifyCompletion
  246. {
  247. readonly UniTask task;
  248. [DebuggerHidden]
  249. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  250. public Awaiter(in UniTask task)
  251. {
  252. this.task = task;
  253. }
  254. public bool IsCompleted
  255. {
  256. [DebuggerHidden]
  257. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  258. get
  259. {
  260. return task.Status.IsCompleted();
  261. }
  262. }
  263. [DebuggerHidden]
  264. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  265. public void GetResult()
  266. {
  267. if (task.source == null) return;
  268. task.source.GetResult(task.token);
  269. }
  270. [DebuggerHidden]
  271. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  272. public void OnCompleted(Action continuation)
  273. {
  274. if (task.source == null)
  275. {
  276. continuation();
  277. }
  278. else
  279. {
  280. task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
  281. }
  282. }
  283. [DebuggerHidden]
  284. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  285. public void UnsafeOnCompleted(Action continuation)
  286. {
  287. if (task.source == null)
  288. {
  289. continuation();
  290. }
  291. else
  292. {
  293. task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
  294. }
  295. }
  296. /// <summary>
  297. /// If register manually continuation, you can use it instead of for compiler OnCompleted methods.
  298. /// </summary>
  299. [DebuggerHidden]
  300. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  301. public void SourceOnCompleted(Action<object> continuation, object state)
  302. {
  303. if (task.source == null)
  304. {
  305. continuation(state);
  306. }
  307. else
  308. {
  309. task.source.OnCompleted(continuation, state, task.token);
  310. }
  311. }
  312. }
  313. }
  314. /// <summary>
  315. /// Lightweight unity specified task-like object.
  316. /// </summary>
  317. [AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder<>))]
  318. [StructLayout(LayoutKind.Auto)]
  319. public readonly struct UniTask<T>
  320. {
  321. readonly IUniTaskSource<T> source;
  322. readonly T result;
  323. readonly short token;
  324. [DebuggerHidden]
  325. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  326. public UniTask(T result)
  327. {
  328. this.source = default;
  329. this.token = default;
  330. this.result = result;
  331. }
  332. [DebuggerHidden]
  333. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  334. public UniTask(IUniTaskSource<T> source, short token)
  335. {
  336. this.source = source;
  337. this.token = token;
  338. this.result = default;
  339. }
  340. public UniTaskStatus Status
  341. {
  342. [DebuggerHidden]
  343. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  344. get
  345. {
  346. return (source == null) ? UniTaskStatus.Succeeded : source.GetStatus(token);
  347. }
  348. }
  349. [DebuggerHidden]
  350. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  351. public Awaiter GetAwaiter()
  352. {
  353. return new Awaiter(this);
  354. }
  355. /// <summary>
  356. /// Memoizing inner IValueTaskSource. The result UniTask can await multiple.
  357. /// </summary>
  358. public UniTask<T> Preserve()
  359. {
  360. if (source == null)
  361. {
  362. return this;
  363. }
  364. else
  365. {
  366. return new UniTask<T>(new MemoizeSource(source), token);
  367. }
  368. }
  369. public UniTask AsUniTask()
  370. {
  371. if (this.source == null) return UniTask.CompletedTask;
  372. var status = this.source.GetStatus(this.token);
  373. if (status.IsCompletedSuccessfully())
  374. {
  375. this.source.GetResult(this.token);
  376. return UniTask.CompletedTask;
  377. }
  378. // Converting UniTask<T> -> UniTask is zero overhead.
  379. return new UniTask(this.source, this.token);
  380. }
  381. public static implicit operator UniTask(UniTask<T> self)
  382. {
  383. return self.AsUniTask();
  384. }
  385. #if !UNITY_2018_3_OR_NEWER
  386. public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self)
  387. {
  388. if (self.source == null)
  389. {
  390. return new System.Threading.Tasks.ValueTask<T>(self.result);
  391. }
  392. #if NETSTANDARD2_0
  393. return self.AsValueTask();
  394. #else
  395. return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);
  396. #endif
  397. }
  398. #endif
  399. /// <summary>
  400. /// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException.
  401. /// </summary>
  402. public UniTask<(bool IsCanceled, T Result)> SuppressCancellationThrow()
  403. {
  404. if (source == null)
  405. {
  406. return new UniTask<(bool IsCanceled, T Result)>((false, result));
  407. }
  408. return new UniTask<(bool, T)>(new IsCanceledSource(source), token);
  409. }
  410. public override string ToString()
  411. {
  412. return (this.source == null) ? result?.ToString()
  413. : "(" + this.source.UnsafeGetStatus() + ")";
  414. }
  415. sealed class IsCanceledSource : IUniTaskSource<(bool, T)>
  416. {
  417. readonly IUniTaskSource<T> source;
  418. [DebuggerHidden]
  419. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  420. public IsCanceledSource(IUniTaskSource<T> source)
  421. {
  422. this.source = source;
  423. }
  424. [DebuggerHidden]
  425. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  426. public (bool, T) GetResult(short token)
  427. {
  428. if (source.GetStatus(token) == UniTaskStatus.Canceled)
  429. {
  430. return (true, default);
  431. }
  432. var result = source.GetResult(token);
  433. return (false, result);
  434. }
  435. [DebuggerHidden]
  436. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  437. void IUniTaskSource.GetResult(short token)
  438. {
  439. GetResult(token);
  440. }
  441. [DebuggerHidden]
  442. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  443. public UniTaskStatus GetStatus(short token)
  444. {
  445. return source.GetStatus(token);
  446. }
  447. [DebuggerHidden]
  448. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  449. public UniTaskStatus UnsafeGetStatus()
  450. {
  451. return source.UnsafeGetStatus();
  452. }
  453. [DebuggerHidden]
  454. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  455. public void OnCompleted(Action<object> continuation, object state, short token)
  456. {
  457. source.OnCompleted(continuation, state, token);
  458. }
  459. }
  460. sealed class MemoizeSource : IUniTaskSource<T>
  461. {
  462. IUniTaskSource<T> source;
  463. T result;
  464. ExceptionDispatchInfo exception;
  465. UniTaskStatus status;
  466. public MemoizeSource(IUniTaskSource<T> source)
  467. {
  468. this.source = source;
  469. }
  470. public T GetResult(short token)
  471. {
  472. if (source == null)
  473. {
  474. if (exception != null)
  475. {
  476. exception.Throw();
  477. }
  478. return result;
  479. }
  480. else
  481. {
  482. try
  483. {
  484. result = source.GetResult(token);
  485. status = UniTaskStatus.Succeeded;
  486. return result;
  487. }
  488. catch (Exception ex)
  489. {
  490. exception = ExceptionDispatchInfo.Capture(ex);
  491. if (ex is OperationCanceledException)
  492. {
  493. status = UniTaskStatus.Canceled;
  494. }
  495. else
  496. {
  497. status = UniTaskStatus.Faulted;
  498. }
  499. throw;
  500. }
  501. finally
  502. {
  503. source = null;
  504. }
  505. }
  506. }
  507. void IUniTaskSource.GetResult(short token)
  508. {
  509. GetResult(token);
  510. }
  511. public UniTaskStatus GetStatus(short token)
  512. {
  513. if (source == null)
  514. {
  515. return status;
  516. }
  517. return source.GetStatus(token);
  518. }
  519. public void OnCompleted(Action<object> continuation, object state, short token)
  520. {
  521. if (source == null)
  522. {
  523. continuation(state);
  524. }
  525. else
  526. {
  527. source.OnCompleted(continuation, state, token);
  528. }
  529. }
  530. public UniTaskStatus UnsafeGetStatus()
  531. {
  532. if (source == null)
  533. {
  534. return status;
  535. }
  536. return source.UnsafeGetStatus();
  537. }
  538. }
  539. public readonly struct Awaiter : ICriticalNotifyCompletion
  540. {
  541. readonly UniTask<T> task;
  542. [DebuggerHidden]
  543. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  544. public Awaiter(in UniTask<T> task)
  545. {
  546. this.task = task;
  547. }
  548. public bool IsCompleted
  549. {
  550. [DebuggerHidden]
  551. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  552. get
  553. {
  554. return task.Status.IsCompleted();
  555. }
  556. }
  557. [DebuggerHidden]
  558. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  559. public T GetResult()
  560. {
  561. var s = task.source;
  562. if (s == null)
  563. {
  564. return task.result;
  565. }
  566. else
  567. {
  568. return s.GetResult(task.token);
  569. }
  570. }
  571. [DebuggerHidden]
  572. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  573. public void OnCompleted(Action continuation)
  574. {
  575. var s = task.source;
  576. if (s == null)
  577. {
  578. continuation();
  579. }
  580. else
  581. {
  582. s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
  583. }
  584. }
  585. [DebuggerHidden]
  586. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  587. public void UnsafeOnCompleted(Action continuation)
  588. {
  589. var s = task.source;
  590. if (s == null)
  591. {
  592. continuation();
  593. }
  594. else
  595. {
  596. s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
  597. }
  598. }
  599. /// <summary>
  600. /// If register manually continuation, you can use it instead of for compiler OnCompleted methods.
  601. /// </summary>
  602. [DebuggerHidden]
  603. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  604. public void SourceOnCompleted(Action<object> continuation, object state)
  605. {
  606. var s = task.source;
  607. if (s == null)
  608. {
  609. continuation(state);
  610. }
  611. else
  612. {
  613. s.OnCompleted(continuation, state, task.token);
  614. }
  615. }
  616. }
  617. }
  618. }