Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

944 wiersze
29 KiB

  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. }
  122. return true;
  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. }
  147. return true;
  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. }
  163. return true;
  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. short version;
  287. AutoResetUniTaskCompletionSource()
  288. {
  289. }
  290. [DebuggerHidden]
  291. public static AutoResetUniTaskCompletionSource Create()
  292. {
  293. if (!pool.TryPop(out var result))
  294. {
  295. result = new AutoResetUniTaskCompletionSource();
  296. }
  297. result.version = result.core.Version;
  298. TaskTracker.TrackActiveTask(result, 2);
  299. return result;
  300. }
  301. [DebuggerHidden]
  302. public static AutoResetUniTaskCompletionSource CreateFromCanceled(CancellationToken cancellationToken, out short token)
  303. {
  304. var source = Create();
  305. source.TrySetCanceled(cancellationToken);
  306. token = source.core.Version;
  307. return source;
  308. }
  309. [DebuggerHidden]
  310. public static AutoResetUniTaskCompletionSource CreateFromException(Exception exception, out short token)
  311. {
  312. var source = Create();
  313. source.TrySetException(exception);
  314. token = source.core.Version;
  315. return source;
  316. }
  317. [DebuggerHidden]
  318. public static AutoResetUniTaskCompletionSource CreateCompleted(out short token)
  319. {
  320. var source = Create();
  321. source.TrySetResult();
  322. token = source.core.Version;
  323. return source;
  324. }
  325. public UniTask Task
  326. {
  327. [DebuggerHidden]
  328. get
  329. {
  330. return new UniTask(this, core.Version);
  331. }
  332. }
  333. [DebuggerHidden]
  334. public bool TrySetResult()
  335. {
  336. return version == core.Version && core.TrySetResult(AsyncUnit.Default);
  337. }
  338. [DebuggerHidden]
  339. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  340. {
  341. return version == core.Version && core.TrySetCanceled(cancellationToken);
  342. }
  343. [DebuggerHidden]
  344. public bool TrySetException(Exception exception)
  345. {
  346. return version == core.Version && core.TrySetException(exception);
  347. }
  348. [DebuggerHidden]
  349. public void GetResult(short token)
  350. {
  351. try
  352. {
  353. core.GetResult(token);
  354. }
  355. finally
  356. {
  357. TryReturn();
  358. }
  359. }
  360. [DebuggerHidden]
  361. public UniTaskStatus GetStatus(short token)
  362. {
  363. return core.GetStatus(token);
  364. }
  365. [DebuggerHidden]
  366. public UniTaskStatus UnsafeGetStatus()
  367. {
  368. return core.UnsafeGetStatus();
  369. }
  370. [DebuggerHidden]
  371. public void OnCompleted(Action<object> continuation, object state, short token)
  372. {
  373. core.OnCompleted(continuation, state, token);
  374. }
  375. [DebuggerHidden]
  376. bool TryReturn()
  377. {
  378. TaskTracker.RemoveTracking(this);
  379. core.Reset();
  380. return pool.TryPush(this);
  381. }
  382. }
  383. public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
  384. {
  385. static TaskPool<AutoResetUniTaskCompletionSource<T>> pool;
  386. AutoResetUniTaskCompletionSource<T> nextNode;
  387. public ref AutoResetUniTaskCompletionSource<T> NextNode => ref nextNode;
  388. static AutoResetUniTaskCompletionSource()
  389. {
  390. TaskPool.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource<T>), () => pool.Size);
  391. }
  392. UniTaskCompletionSourceCore<T> core;
  393. short version;
  394. AutoResetUniTaskCompletionSource()
  395. {
  396. }
  397. [DebuggerHidden]
  398. public static AutoResetUniTaskCompletionSource<T> Create()
  399. {
  400. if (!pool.TryPop(out var result))
  401. {
  402. result = new AutoResetUniTaskCompletionSource<T>();
  403. }
  404. result.version = result.core.Version;
  405. TaskTracker.TrackActiveTask(result, 2);
  406. return result;
  407. }
  408. [DebuggerHidden]
  409. public static AutoResetUniTaskCompletionSource<T> CreateFromCanceled(CancellationToken cancellationToken, out short token)
  410. {
  411. var source = Create();
  412. source.TrySetCanceled(cancellationToken);
  413. token = source.core.Version;
  414. return source;
  415. }
  416. [DebuggerHidden]
  417. public static AutoResetUniTaskCompletionSource<T> CreateFromException(Exception exception, out short token)
  418. {
  419. var source = Create();
  420. source.TrySetException(exception);
  421. token = source.core.Version;
  422. return source;
  423. }
  424. [DebuggerHidden]
  425. public static AutoResetUniTaskCompletionSource<T> CreateFromResult(T result, out short token)
  426. {
  427. var source = Create();
  428. source.TrySetResult(result);
  429. token = source.core.Version;
  430. return source;
  431. }
  432. public UniTask<T> Task
  433. {
  434. [DebuggerHidden]
  435. get
  436. {
  437. return new UniTask<T>(this, core.Version);
  438. }
  439. }
  440. [DebuggerHidden]
  441. public bool TrySetResult(T result)
  442. {
  443. return version == core.Version && core.TrySetResult(result);
  444. }
  445. [DebuggerHidden]
  446. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  447. {
  448. return version == core.Version && core.TrySetCanceled(cancellationToken);
  449. }
  450. [DebuggerHidden]
  451. public bool TrySetException(Exception exception)
  452. {
  453. return version == core.Version && core.TrySetException(exception);
  454. }
  455. [DebuggerHidden]
  456. public T GetResult(short token)
  457. {
  458. try
  459. {
  460. return core.GetResult(token);
  461. }
  462. finally
  463. {
  464. TryReturn();
  465. }
  466. }
  467. [DebuggerHidden]
  468. void IUniTaskSource.GetResult(short token)
  469. {
  470. GetResult(token);
  471. }
  472. [DebuggerHidden]
  473. public UniTaskStatus GetStatus(short token)
  474. {
  475. return core.GetStatus(token);
  476. }
  477. [DebuggerHidden]
  478. public UniTaskStatus UnsafeGetStatus()
  479. {
  480. return core.UnsafeGetStatus();
  481. }
  482. [DebuggerHidden]
  483. public void OnCompleted(Action<object> continuation, object state, short token)
  484. {
  485. core.OnCompleted(continuation, state, token);
  486. }
  487. [DebuggerHidden]
  488. bool TryReturn()
  489. {
  490. TaskTracker.RemoveTracking(this);
  491. core.Reset();
  492. return pool.TryPush(this);
  493. }
  494. }
  495. public class UniTaskCompletionSource : IUniTaskSource, IPromise
  496. {
  497. CancellationToken cancellationToken;
  498. ExceptionHolder exception;
  499. object gate;
  500. Action<object> singleContinuation;
  501. object singleState;
  502. List<(Action<object>, object)> secondaryContinuationList;
  503. int intStatus; // UniTaskStatus
  504. bool handled = false;
  505. public UniTaskCompletionSource()
  506. {
  507. TaskTracker.TrackActiveTask(this, 2);
  508. }
  509. [DebuggerHidden]
  510. internal void MarkHandled()
  511. {
  512. if (!handled)
  513. {
  514. handled = true;
  515. TaskTracker.RemoveTracking(this);
  516. }
  517. }
  518. public UniTask Task
  519. {
  520. [DebuggerHidden]
  521. get
  522. {
  523. return new UniTask(this, 0);
  524. }
  525. }
  526. [DebuggerHidden]
  527. public bool TrySetResult()
  528. {
  529. return TrySignalCompletion(UniTaskStatus.Succeeded);
  530. }
  531. [DebuggerHidden]
  532. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  533. {
  534. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  535. this.cancellationToken = cancellationToken;
  536. return TrySignalCompletion(UniTaskStatus.Canceled);
  537. }
  538. [DebuggerHidden]
  539. public bool TrySetException(Exception exception)
  540. {
  541. if (exception is OperationCanceledException oce)
  542. {
  543. return TrySetCanceled(oce.CancellationToken);
  544. }
  545. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  546. this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
  547. return TrySignalCompletion(UniTaskStatus.Faulted);
  548. }
  549. [DebuggerHidden]
  550. public void GetResult(short token)
  551. {
  552. MarkHandled();
  553. var status = (UniTaskStatus)intStatus;
  554. switch (status)
  555. {
  556. case UniTaskStatus.Succeeded:
  557. return;
  558. case UniTaskStatus.Faulted:
  559. exception.GetException().Throw();
  560. return;
  561. case UniTaskStatus.Canceled:
  562. throw new OperationCanceledException(cancellationToken);
  563. default:
  564. case UniTaskStatus.Pending:
  565. throw new InvalidOperationException("not yet completed.");
  566. }
  567. }
  568. [DebuggerHidden]
  569. public UniTaskStatus GetStatus(short token)
  570. {
  571. return (UniTaskStatus)intStatus;
  572. }
  573. [DebuggerHidden]
  574. public UniTaskStatus UnsafeGetStatus()
  575. {
  576. return (UniTaskStatus)intStatus;
  577. }
  578. [DebuggerHidden]
  579. public void OnCompleted(Action<object> continuation, object state, short token)
  580. {
  581. if (gate == null)
  582. {
  583. Interlocked.CompareExchange(ref gate, new object(), null);
  584. }
  585. var lockGate = Thread.VolatileRead(ref gate);
  586. lock (lockGate) // wait TrySignalCompletion, after status is not pending.
  587. {
  588. if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
  589. {
  590. continuation(state);
  591. return;
  592. }
  593. if (singleContinuation == null)
  594. {
  595. singleContinuation = continuation;
  596. singleState = state;
  597. }
  598. else
  599. {
  600. if (secondaryContinuationList == null)
  601. {
  602. secondaryContinuationList = new List<(Action<object>, object)>();
  603. }
  604. secondaryContinuationList.Add((continuation, state));
  605. }
  606. }
  607. }
  608. [DebuggerHidden]
  609. bool TrySignalCompletion(UniTaskStatus status)
  610. {
  611. if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
  612. {
  613. if (gate == null)
  614. {
  615. Interlocked.CompareExchange(ref gate, new object(), null);
  616. }
  617. var lockGate = Thread.VolatileRead(ref gate);
  618. lock (lockGate) // wait OnCompleted.
  619. {
  620. if (singleContinuation != null)
  621. {
  622. try
  623. {
  624. singleContinuation(singleState);
  625. }
  626. catch (Exception ex)
  627. {
  628. UniTaskScheduler.PublishUnobservedTaskException(ex);
  629. }
  630. }
  631. if (secondaryContinuationList != null)
  632. {
  633. foreach (var (c, state) in secondaryContinuationList)
  634. {
  635. try
  636. {
  637. c(state);
  638. }
  639. catch (Exception ex)
  640. {
  641. UniTaskScheduler.PublishUnobservedTaskException(ex);
  642. }
  643. }
  644. }
  645. singleContinuation = null;
  646. singleState = null;
  647. secondaryContinuationList = null;
  648. }
  649. return true;
  650. }
  651. return false;
  652. }
  653. }
  654. public class UniTaskCompletionSource<T> : IUniTaskSource<T>, IPromise<T>
  655. {
  656. CancellationToken cancellationToken;
  657. T result;
  658. ExceptionHolder exception;
  659. object gate;
  660. Action<object> singleContinuation;
  661. object singleState;
  662. List<(Action<object>, object)> secondaryContinuationList;
  663. int intStatus; // UniTaskStatus
  664. bool handled = false;
  665. public UniTaskCompletionSource()
  666. {
  667. TaskTracker.TrackActiveTask(this, 2);
  668. }
  669. [DebuggerHidden]
  670. internal void MarkHandled()
  671. {
  672. if (!handled)
  673. {
  674. handled = true;
  675. TaskTracker.RemoveTracking(this);
  676. }
  677. }
  678. public UniTask<T> Task
  679. {
  680. [DebuggerHidden]
  681. get
  682. {
  683. return new UniTask<T>(this, 0);
  684. }
  685. }
  686. [DebuggerHidden]
  687. public bool TrySetResult(T result)
  688. {
  689. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  690. this.result = result;
  691. return TrySignalCompletion(UniTaskStatus.Succeeded);
  692. }
  693. [DebuggerHidden]
  694. public bool TrySetCanceled(CancellationToken cancellationToken = default)
  695. {
  696. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  697. this.cancellationToken = cancellationToken;
  698. return TrySignalCompletion(UniTaskStatus.Canceled);
  699. }
  700. [DebuggerHidden]
  701. public bool TrySetException(Exception exception)
  702. {
  703. if (exception is OperationCanceledException oce)
  704. {
  705. return TrySetCanceled(oce.CancellationToken);
  706. }
  707. if (UnsafeGetStatus() != UniTaskStatus.Pending) return false;
  708. this.exception = new ExceptionHolder(ExceptionDispatchInfo.Capture(exception));
  709. return TrySignalCompletion(UniTaskStatus.Faulted);
  710. }
  711. [DebuggerHidden]
  712. public T GetResult(short token)
  713. {
  714. MarkHandled();
  715. var status = (UniTaskStatus)intStatus;
  716. switch (status)
  717. {
  718. case UniTaskStatus.Succeeded:
  719. return result;
  720. case UniTaskStatus.Faulted:
  721. exception.GetException().Throw();
  722. return default;
  723. case UniTaskStatus.Canceled:
  724. throw new OperationCanceledException(cancellationToken);
  725. default:
  726. case UniTaskStatus.Pending:
  727. throw new InvalidOperationException("not yet completed.");
  728. }
  729. }
  730. [DebuggerHidden]
  731. void IUniTaskSource.GetResult(short token)
  732. {
  733. GetResult(token);
  734. }
  735. [DebuggerHidden]
  736. public UniTaskStatus GetStatus(short token)
  737. {
  738. return (UniTaskStatus)intStatus;
  739. }
  740. [DebuggerHidden]
  741. public UniTaskStatus UnsafeGetStatus()
  742. {
  743. return (UniTaskStatus)intStatus;
  744. }
  745. [DebuggerHidden]
  746. public void OnCompleted(Action<object> continuation, object state, short token)
  747. {
  748. if (gate == null)
  749. {
  750. Interlocked.CompareExchange(ref gate, new object(), null);
  751. }
  752. var lockGate = Thread.VolatileRead(ref gate);
  753. lock (lockGate) // wait TrySignalCompletion, after status is not pending.
  754. {
  755. if ((UniTaskStatus)intStatus != UniTaskStatus.Pending)
  756. {
  757. continuation(state);
  758. return;
  759. }
  760. if (singleContinuation == null)
  761. {
  762. singleContinuation = continuation;
  763. singleState = state;
  764. }
  765. else
  766. {
  767. if (secondaryContinuationList == null)
  768. {
  769. secondaryContinuationList = new List<(Action<object>, object)>();
  770. }
  771. secondaryContinuationList.Add((continuation, state));
  772. }
  773. }
  774. }
  775. [DebuggerHidden]
  776. bool TrySignalCompletion(UniTaskStatus status)
  777. {
  778. if (Interlocked.CompareExchange(ref intStatus, (int)status, (int)UniTaskStatus.Pending) == (int)UniTaskStatus.Pending)
  779. {
  780. if (gate == null)
  781. {
  782. Interlocked.CompareExchange(ref gate, new object(), null);
  783. }
  784. var lockGate = Thread.VolatileRead(ref gate);
  785. lock (lockGate) // wait OnCompleted.
  786. {
  787. if (singleContinuation != null)
  788. {
  789. try
  790. {
  791. singleContinuation(singleState);
  792. }
  793. catch (Exception ex)
  794. {
  795. UniTaskScheduler.PublishUnobservedTaskException(ex);
  796. }
  797. }
  798. if (secondaryContinuationList != null)
  799. {
  800. foreach (var (c, state) in secondaryContinuationList)
  801. {
  802. try
  803. {
  804. c(state);
  805. }
  806. catch (Exception ex)
  807. {
  808. UniTaskScheduler.PublishUnobservedTaskException(ex);
  809. }
  810. }
  811. }
  812. singleContinuation = null;
  813. singleState = null;
  814. secondaryContinuationList = null;
  815. }
  816. return true;
  817. }
  818. return false;
  819. }
  820. }
  821. }