UniTaskCompletionSource.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.ExceptionServices;
  7. using System.Runtime.InteropServices;
  8. using System.Threading;
  9. using Cysharp.Threading.Tasks.Internal;
  10. namespace Cysharp.Threading.Tasks
  11. {
  12. public interface IResolvePromise
  13. {
  14. bool TrySetResult();
  15. }
  16. public interface IResolvePromise<T>
  17. {
  18. bool TrySetResult(T value);
  19. }
  20. public interface IRejectPromise
  21. {
  22. bool TrySetException(Exception exception);
  23. }
  24. public interface ICancelPromise
  25. {
  26. bool TrySetCanceled(CancellationToken cancellationToken = default);
  27. }
  28. public interface IPromise<T> : IResolvePromise<T>, IRejectPromise, ICancelPromise
  29. {
  30. }
  31. public interface IPromise : IResolvePromise, IRejectPromise, ICancelPromise
  32. {
  33. }
  34. internal class ExceptionHolder
  35. {
  36. ExceptionDispatchInfo exception;
  37. bool calledGet = false;
  38. public ExceptionHolder(ExceptionDispatchInfo exception)
  39. {
  40. this.exception = exception;
  41. }
  42. public ExceptionDispatchInfo GetException()
  43. {
  44. if (!calledGet)
  45. {
  46. calledGet = true;
  47. GC.SuppressFinalize(this);
  48. }
  49. return exception;
  50. }
  51. ~ExceptionHolder()
  52. {
  53. if (!calledGet)
  54. {
  55. UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
  56. }
  57. }
  58. }
  59. [StructLayout(LayoutKind.Auto)]
  60. public struct UniTaskCompletionSourceCore<TResult>
  61. {
  62. // Struct Size: TResult + (8 + 2 + 1 + 1 + 8 + 8)
  63. TResult result;
  64. object error; // ExceptionHolder or OperationCanceledException
  65. short version;
  66. bool hasUnhandledError;
  67. int completedCount; // 0: completed == false
  68. Action<object> continuation;
  69. object continuationState;
  70. [DebuggerHidden]
  71. public void Reset()
  72. {
  73. ReportUnhandledError();
  74. unchecked
  75. {
  76. version += 1; // incr version.
  77. }
  78. completedCount = 0;
  79. result = default;
  80. error = null;
  81. hasUnhandledError = false;
  82. continuation = null;
  83. continuationState = null;
  84. }
  85. void ReportUnhandledError()
  86. {
  87. if (hasUnhandledError)
  88. {
  89. try
  90. {
  91. if (error is OperationCanceledException oc)
  92. {
  93. UniTaskScheduler.PublishUnobservedTaskException(oc);
  94. }
  95. else if (error is ExceptionHolder e)
  96. {
  97. UniTaskScheduler.PublishUnobservedTaskException(e.GetException().SourceException);
  98. }
  99. }
  100. catch
  101. {
  102. }
  103. }
  104. }
  105. internal void MarkHandled()
  106. {
  107. hasUnhandledError = false;
  108. }
  109. /// <summary>Completes with a successful result.</summary>
  110. /// <param name="result">The result.</param>
  111. [DebuggerHidden]
  112. public bool TrySetResult(TResult result)
  113. {
  114. if (Interlocked.Increment(ref completedCount) == 1)
  115. {
  116. // setup result
  117. this.result = result;
  118. if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
  119. {
  120. continuation(continuationState);
  121. return true;
  122. }
  123. }
  124. return false;
  125. }
  126. /// <summary>Completes with an error.</summary>
  127. /// <param name="error">The exception.</param>
  128. [DebuggerHidden]
  129. public bool TrySetException(Exception error)
  130. {
  131. if (Interlocked.Increment(ref completedCount) == 1)
  132. {
  133. // setup result
  134. this.hasUnhandledError = true;
  135. if (error is OperationCanceledException)
  136. {
  137. this.error = error;
  138. }
  139. else
  140. {
  141. this.error = new ExceptionHolder(ExceptionDispatchInfo.Capture(error));
  142. }
  143. if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
  144. {
  145. continuation(continuationState);
  146. return true;
  147. }
  148. }
  149. return false;
  150. }
  151. [DebuggerHidden]
  152. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  153. {
  154. if (Interlocked.Increment(ref completedCount) == 1)
  155. {
  156. // setup result
  157. this.hasUnhandledError = true;
  158. this.error = new OperationCanceledException(cancellationToken);
  159. if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
  160. {
  161. continuation(continuationState);
  162. return true;
  163. }
  164. }
  165. return false;
  166. }
  167. /// <summary>Gets the operation version.</summary>
  168. [DebuggerHidden]
  169. public short Version => version;
  170. /// <summary>Gets the status of the operation.</summary>
  171. /// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
  172. [DebuggerHidden]
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public UniTaskStatus GetStatus(short token)
  175. {
  176. ValidateToken(token);
  177. return (continuation == null || (completedCount == 0)) ? UniTaskStatus.Pending
  178. : (error == null) ? UniTaskStatus.Succeeded
  179. : (error is OperationCanceledException) ? UniTaskStatus.Canceled
  180. : UniTaskStatus.Faulted;
  181. }
  182. /// <summary>Gets the status of the operation without token validation.</summary>
  183. [DebuggerHidden]
  184. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  185. public UniTaskStatus UnsafeGetStatus()
  186. {
  187. return (continuation == null || (completedCount == 0)) ? UniTaskStatus.Pending
  188. : (error == null) ? UniTaskStatus.Succeeded
  189. : (error is OperationCanceledException) ? UniTaskStatus.Canceled
  190. : UniTaskStatus.Faulted;
  191. }
  192. /// <summary>Gets the result of the operation.</summary>
  193. /// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
  194. // [StackTraceHidden]
  195. [DebuggerHidden]
  196. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  197. public TResult GetResult(short token)
  198. {
  199. ValidateToken(token);
  200. if (completedCount == 0)
  201. {
  202. throw new InvalidOperationException("Not yet completed, UniTask only allow to use await.");
  203. }
  204. if (error != null)
  205. {
  206. hasUnhandledError = false;
  207. if (error is OperationCanceledException oce)
  208. {
  209. throw oce;
  210. }
  211. else if (error is ExceptionHolder eh)
  212. {
  213. eh.GetException().Throw();
  214. }
  215. throw new InvalidOperationException("Critical: invalid exception type was held.");
  216. }
  217. return result;
  218. }
  219. /// <summary>Schedules the continuation action for this operation.</summary>
  220. /// <param name="continuation">The continuation to invoke when the operation has completed.</param>
  221. /// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param>
  222. /// <param name="token">Opaque value that was provided to the <see cref="UniTask"/>'s constructor.</param>
  223. [DebuggerHidden]
  224. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  225. public void OnCompleted(Action<object> continuation, object state, short token /*, ValueTaskSourceOnCompletedFlags flags */)
  226. {
  227. if (continuation == null)
  228. {
  229. throw new ArgumentNullException(nameof(continuation));
  230. }
  231. ValidateToken(token);
  232. /* no use ValueTaskSourceOnCOmpletedFlags, always no capture ExecutionContext and SynchronizationContext. */
  233. /*
  234. PatternA: GetStatus=Pending => OnCompleted => TrySet*** => GetResult
  235. PatternB: TrySet*** => GetStatus=!Pending => GetResult
  236. PatternC: GetStatus=Pending => TrySet/OnCompleted(race condition) => GetResult
  237. C.1: win OnCompleted -> TrySet invoke saved continuation
  238. C.2: win TrySet -> should invoke continuation here.
  239. */
  240. // not set continuation yet.
  241. object oldContinuation = this.continuation;
  242. if (oldContinuation == null)
  243. {
  244. continuationState = state;
  245. oldContinuation = Interlocked.CompareExchange(ref this.continuation, continuation, null);
  246. }
  247. if (oldContinuation != null)
  248. {
  249. // already running continuation in TrySet.
  250. // It will cause call OnCompleted multiple time, invalid.
  251. if (!ReferenceEquals(oldContinuation, UniTaskCompletionSourceCoreShared.s_sentinel))
  252. {
  253. throw new InvalidOperationException("Already continuation registered, can not await twice or get Status after await.");
  254. }
  255. continuation(state);
  256. }
  257. }
  258. [DebuggerHidden]
  259. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  260. private void ValidateToken(short token)
  261. {
  262. if (token != version)
  263. {
  264. throw new InvalidOperationException("Token version is not matched, can not await twice or get Status after await.");
  265. }
  266. }
  267. }
  268. internal static class UniTaskCompletionSourceCoreShared // separated out of generic to avoid unnecessary duplication
  269. {
  270. internal static readonly Action<object> s_sentinel = CompletionSentinel;
  271. private static void CompletionSentinel(object _) // named method to aid debugging
  272. {
  273. throw new InvalidOperationException("The sentinel delegate should never be invoked.");
  274. }
  275. }
  276. public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
  277. {
  278. static TaskPool<AutoResetUniTaskCompletionSource> pool;
  279. AutoResetUniTaskCompletionSource nextNode;
  280. public ref AutoResetUniTaskCompletionSource NextNode => ref nextNode;
  281. static AutoResetUniTaskCompletionSource()
  282. {
  283. TaskPool.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource), () => pool.Size);
  284. }
  285. UniTaskCompletionSourceCore<AsyncUnit> core;
  286. AutoResetUniTaskCompletionSource()
  287. {
  288. }
  289. [DebuggerHidden]
  290. public static AutoResetUniTaskCompletionSource Create()
  291. {
  292. if (!pool.TryPop(out var result))
  293. {
  294. result = new AutoResetUniTaskCompletionSource();
  295. }
  296. TaskTracker.TrackActiveTask(result, 2);
  297. return result;
  298. }
  299. [DebuggerHidden]
  300. public static AutoResetUniTaskCompletionSource CreateFromCanceled(CancellationToken cancellationToken, out short token)
  301. {
  302. var source = Create();
  303. source.TrySetCanceled(cancellationToken);
  304. token = source.core.Version;
  305. return source;
  306. }
  307. [DebuggerHidden]
  308. public static AutoResetUniTaskCompletionSource CreateFromException(Exception exception, out short token)
  309. {
  310. var source = Create();
  311. source.TrySetException(exception);
  312. token = source.core.Version;
  313. return source;
  314. }
  315. [DebuggerHidden]
  316. public static AutoResetUniTaskCompletionSource CreateCompleted(out short token)
  317. {
  318. var source = Create();
  319. source.TrySetResult();
  320. token = source.core.Version;
  321. return source;
  322. }
  323. public UniTask Task
  324. {
  325. [DebuggerHidden]
  326. get
  327. {
  328. return new UniTask(this, core.Version);
  329. }
  330. }
  331. [DebuggerHidden]
  332. public bool TrySetResult()
  333. {
  334. return core.TrySetResult(AsyncUnit.Default);
  335. }
  336. [DebuggerHidden]
  337. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  338. {
  339. return core.TrySetCanceled(cancellationToken);
  340. }
  341. [DebuggerHidden]
  342. public bool TrySetException(Exception exception)
  343. {
  344. return core.TrySetException(exception);
  345. }
  346. [DebuggerHidden]
  347. public void GetResult(short token)
  348. {
  349. try
  350. {
  351. core.GetResult(token);
  352. }
  353. finally
  354. {
  355. TryReturn();
  356. }
  357. }
  358. [DebuggerHidden]
  359. public UniTaskStatus GetStatus(short token)
  360. {
  361. return core.GetStatus(token);
  362. }
  363. [DebuggerHidden]
  364. public UniTaskStatus UnsafeGetStatus()
  365. {
  366. return core.UnsafeGetStatus();
  367. }
  368. [DebuggerHidden]
  369. public void OnCompleted(Action<object> continuation, object state, short token)
  370. {
  371. core.OnCompleted(continuation, state, token);
  372. }
  373. [DebuggerHidden]
  374. bool TryReturn()
  375. {
  376. TaskTracker.RemoveTracking(this);
  377. core.Reset();
  378. return pool.TryPush(this);
  379. }
  380. }
  381. public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
  382. {
  383. static TaskPool<AutoResetUniTaskCompletionSource<T>> pool;
  384. AutoResetUniTaskCompletionSource<T> nextNode;
  385. public ref AutoResetUniTaskCompletionSource<T> NextNode => ref nextNode;
  386. static AutoResetUniTaskCompletionSource()
  387. {
  388. TaskPool.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource<T>), () => pool.Size);
  389. }
  390. UniTaskCompletionSourceCore<T> core;
  391. AutoResetUniTaskCompletionSource()
  392. {
  393. }
  394. [DebuggerHidden]
  395. public static AutoResetUniTaskCompletionSource<T> Create()
  396. {
  397. if (!pool.TryPop(out var result))
  398. {
  399. result = new AutoResetUniTaskCompletionSource<T>();
  400. }
  401. TaskTracker.TrackActiveTask(result, 2);
  402. return result;
  403. }
  404. [DebuggerHidden]
  405. public static AutoResetUniTaskCompletionSource<T> CreateFromCanceled(CancellationToken cancellationToken, out short token)
  406. {
  407. var source = Create();
  408. source.TrySetCanceled(cancellationToken);
  409. token = source.core.Version;
  410. return source;
  411. }
  412. [DebuggerHidden]
  413. public static AutoResetUniTaskCompletionSource<T> CreateFromException(Exception exception, out short token)
  414. {
  415. var source = Create();
  416. source.TrySetException(exception);
  417. token = source.core.Version;
  418. return source;
  419. }
  420. [DebuggerHidden]
  421. public static AutoResetUniTaskCompletionSource<T> CreateFromResult(T result, out short token)
  422. {
  423. var source = Create();
  424. source.TrySetResult(result);
  425. token = source.core.Version;
  426. return source;
  427. }
  428. public UniTask<T> Task
  429. {
  430. [DebuggerHidden]
  431. get
  432. {
  433. return new UniTask<T>(this, core.Version);
  434. }
  435. }
  436. [DebuggerHidden]
  437. public bool TrySetResult(T result)
  438. {
  439. return core.TrySetResult(result);
  440. }
  441. [DebuggerHidden]
  442. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  443. {
  444. return core.TrySetCanceled(cancellationToken);
  445. }
  446. [DebuggerHidden]
  447. public bool TrySetException(Exception exception)
  448. {
  449. return core.TrySetException(exception);
  450. }
  451. [DebuggerHidden]
  452. public T GetResult(short token)
  453. {
  454. try
  455. {
  456. return core.GetResult(token);
  457. }
  458. finally
  459. {
  460. TryReturn();
  461. }
  462. }
  463. [DebuggerHidden]
  464. void IUniTaskSource.GetResult(short token)
  465. {
  466. GetResult(token);
  467. }
  468. [DebuggerHidden]
  469. public UniTaskStatus GetStatus(short token)
  470. {
  471. return core.GetStatus(token);
  472. }
  473. [DebuggerHidden]
  474. public UniTaskStatus UnsafeGetStatus()
  475. {
  476. return core.UnsafeGetStatus();
  477. }
  478. [DebuggerHidden]
  479. public void OnCompleted(Action<object> continuation, object state, short token)
  480. {
  481. core.OnCompleted(continuation, state, token);
  482. }
  483. [DebuggerHidden]
  484. bool TryReturn()
  485. {
  486. TaskTracker.RemoveTracking(this);
  487. core.Reset();
  488. return pool.TryPush(this);
  489. }
  490. }
  491. public class UniTaskCompletionSource : IUniTaskSource, IPromise
  492. {
  493. CancellationToken cancellationToken;
  494. ExceptionHolder exception;
  495. object gate;
  496. Action<object> singleContinuation;
  497. object singleState;
  498. List<(Action<object>, object)> secondaryContinuationList;
  499. int intStatus; // UniTaskStatus
  500. bool handled = false;
  501. public UniTaskCompletionSource()
  502. {
  503. TaskTracker.TrackActiveTask(this, 2);
  504. }
  505. [DebuggerHidden]
  506. internal void MarkHandled()
  507. {
  508. if (!handled)
  509. {
  510. handled = true;
  511. TaskTracker.RemoveTracking(this);
  512. }
  513. }
  514. public UniTask Task
  515. {
  516. [DebuggerHidden]
  517. get
  518. {
  519. return new UniTask(this, 0);
  520. }
  521. }
  522. [DebuggerHidden]
  523. public bool TrySetResult()
  524. {
  525. return TrySignalCompletion(UniTaskStatus.Succeeded);
  526. }
  527. [DebuggerHidden]
  528. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  529. {
  530. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  531. this.cancellationToken = cancellationToken;
  532. return TrySignalCompletion(UniTaskStatus.Canceled);
  533. }
  534. [DebuggerHidden]
  535. public bool TrySetException(Exception exception)
  536. {
  537. if (exception is OperationCanceledException oce)
  538. {
  539. return TrySetCanceled(oce.CancellationToken);
  540. }
  541. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  542. this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
  543. return TrySignalCompletion(UniTaskStatus.Faulted);
  544. }
  545. [DebuggerHidden]
  546. public void GetResult(short token)
  547. {
  548. MarkHandled();
  549. var status = (UniTaskStatus)intStatus;
  550. switch (status)
  551. {
  552. case UniTaskStatus.Succeeded:
  553. return;
  554. case UniTaskStatus.Faulted:
  555. exception.GetException().Throw();
  556. return;
  557. case UniTaskStatus.Canceled:
  558. throw new OperationCanceledException(cancellationToken);
  559. default:
  560. case UniTaskStatus.Pending:
  561. throw new InvalidOperationException("not yet completed.");
  562. }
  563. }
  564. [DebuggerHidden]
  565. public UniTaskStatus GetStatus(short token)
  566. {
  567. return (UniTaskStatus)intStatus;
  568. }
  569. [DebuggerHidden]
  570. public UniTaskStatus UnsafeGetStatus()
  571. {
  572. return (UniTaskStatus)intStatus;
  573. }
  574. [DebuggerHidden]
  575. public void OnCompleted(Action<object> continuation, object state, short token)
  576. {
  577. if (gate == null)
  578. {
  579. Interlocked.CompareExchange(ref gate, new object(), null);
  580. }
  581. var lockGate = Thread.VolatileRead(ref gate);
  582. lock (lockGate) // wait TrySignalCompletion, after status is not pending.
  583. {
  584. if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
  585. {
  586. continuation(state);
  587. return;
  588. }
  589. if (singleContinuation == null)
  590. {
  591. singleContinuation = continuation;
  592. singleState = state;
  593. }
  594. else
  595. {
  596. if (secondaryContinuationList == null)
  597. {
  598. secondaryContinuationList = new List<(Action<object>, object)>();
  599. }
  600. secondaryContinuationList.Add((continuation, state));
  601. }
  602. }
  603. }
  604. [DebuggerHidden]
  605. bool TrySignalCompletion(UniTaskStatus status)
  606. {
  607. if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
  608. {
  609. if (gate == null)
  610. {
  611. Interlocked.CompareExchange(ref gate, new object(), null);
  612. }
  613. var lockGate = Thread.VolatileRead(ref gate);
  614. lock (lockGate) // wait OnCompleted.
  615. {
  616. if (singleContinuation != null)
  617. {
  618. try
  619. {
  620. singleContinuation(singleState);
  621. }
  622. catch (Exception ex)
  623. {
  624. UniTaskScheduler.PublishUnobservedTaskException(ex);
  625. }
  626. }
  627. if (secondaryContinuationList != null)
  628. {
  629. foreach (var (c, state) in secondaryContinuationList)
  630. {
  631. try
  632. {
  633. c(state);
  634. }
  635. catch (Exception ex)
  636. {
  637. UniTaskScheduler.PublishUnobservedTaskException(ex);
  638. }
  639. }
  640. }
  641. singleContinuation = null;
  642. singleState = null;
  643. secondaryContinuationList = null;
  644. }
  645. return true;
  646. }
  647. return false;
  648. }
  649. }
  650. public class UniTaskCompletionSource<T> : IUniTaskSource<T>, IPromise<T>
  651. {
  652. CancellationToken cancellationToken;
  653. T result;
  654. ExceptionHolder exception;
  655. object gate;
  656. Action<object> singleContinuation;
  657. object singleState;
  658. List<(Action<object>, object)> secondaryContinuationList;
  659. int intStatus; // UniTaskStatus
  660. bool handled = false;
  661. public UniTaskCompletionSource()
  662. {
  663. TaskTracker.TrackActiveTask(this, 2);
  664. }
  665. [DebuggerHidden]
  666. internal void MarkHandled()
  667. {
  668. if (!handled)
  669. {
  670. handled = true;
  671. TaskTracker.RemoveTracking(this);
  672. }
  673. }
  674. public UniTask<T> Task
  675. {
  676. [DebuggerHidden]
  677. get
  678. {
  679. return new UniTask<T>(this, 0);
  680. }
  681. }
  682. [DebuggerHidden]
  683. public bool TrySetResult(T result)
  684. {
  685. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  686. this.result = result;
  687. return TrySignalCompletion(UniTaskStatus.Succeeded);
  688. }
  689. [DebuggerHidden]
  690. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  691. {
  692. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  693. this.cancellationToken = cancellationToken;
  694. return TrySignalCompletion(UniTaskStatus.Canceled);
  695. }
  696. [DebuggerHidden]
  697. public bool TrySetException(Exception exception)
  698. {
  699. if (exception is OperationCanceledException oce)
  700. {
  701. return TrySetCanceled(oce.CancellationToken);
  702. }
  703. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  704. this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
  705. return TrySignalCompletion(UniTaskStatus.Faulted);
  706. }
  707. [DebuggerHidden]
  708. public T GetResult(short token)
  709. {
  710. MarkHandled();
  711. var status = (UniTaskStatus)intStatus;
  712. switch (status)
  713. {
  714. case UniTaskStatus.Succeeded:
  715. return result;
  716. case UniTaskStatus.Faulted:
  717. exception.GetException().Throw();
  718. return default;
  719. case UniTaskStatus.Canceled:
  720. throw new OperationCanceledException(cancellationToken);
  721. default:
  722. case UniTaskStatus.Pending:
  723. throw new InvalidOperationException("not yet completed.");
  724. }
  725. }
  726. [DebuggerHidden]
  727. void IUniTaskSource.GetResult(short token)
  728. {
  729. GetResult(token);
  730. }
  731. [DebuggerHidden]
  732. public UniTaskStatus GetStatus(short token)
  733. {
  734. return (UniTaskStatus)intStatus;
  735. }
  736. [DebuggerHidden]
  737. public UniTaskStatus UnsafeGetStatus()
  738. {
  739. return (UniTaskStatus)intStatus;
  740. }
  741. [DebuggerHidden]
  742. public void OnCompleted(Action<object> continuation, object state, short token)
  743. {
  744. if (gate == null)
  745. {
  746. Interlocked.CompareExchange(ref gate, new object(), null);
  747. }
  748. var lockGate = Thread.VolatileRead(ref gate);
  749. lock (lockGate) // wait TrySignalCompletion, after status is not pending.
  750. {
  751. if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
  752. {
  753. continuation(state);
  754. return;
  755. }
  756. if (singleContinuation == null)
  757. {
  758. singleContinuation = continuation;
  759. singleState = state;
  760. }
  761. else
  762. {
  763. if (secondaryContinuationList == null)
  764. {
  765. secondaryContinuationList = new List<(Action<object>, object)>();
  766. }
  767. secondaryContinuationList.Add((continuation, state));
  768. }
  769. }
  770. }
  771. [DebuggerHidden]
  772. bool TrySignalCompletion(UniTaskStatus status)
  773. {
  774. if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
  775. {
  776. if (gate == null)
  777. {
  778. Interlocked.CompareExchange(ref gate, new object(), null);
  779. }
  780. var lockGate = Thread.VolatileRead(ref gate);
  781. lock (lockGate) // wait OnCompleted.
  782. {
  783. if (singleContinuation != null)
  784. {
  785. try
  786. {
  787. singleContinuation(singleState);
  788. }
  789. catch (Exception ex)
  790. {
  791. UniTaskScheduler.PublishUnobservedTaskException(ex);
  792. }
  793. }
  794. if (secondaryContinuationList != null)
  795. {
  796. foreach (var (c, state) in secondaryContinuationList)
  797. {
  798. try
  799. {
  800. c(state);
  801. }
  802. catch (Exception ex)
  803. {
  804. UniTaskScheduler.PublishUnobservedTaskException(ex);
  805. }
  806. }
  807. }
  808. singleContinuation = null;
  809. singleState = null;
  810. secondaryContinuationList = null;
  811. }
  812. return true;
  813. }
  814. return false;
  815. }
  816. }
  817. }