CancellationTokenExtensions.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Runtime.CompilerServices;
  4. using System.Threading;
  5. namespace Cysharp.Threading.Tasks
  6. {
  7. public static class CancellationTokenExtensions
  8. {
  9. static readonly Action<object> cancellationTokenCallback = Callback;
  10. static readonly Action<object> disposeCallback = DisposeCallback;
  11. public static CancellationToken ToCancellationToken(this UniTask task)
  12. {
  13. var cts = new CancellationTokenSource();
  14. ToCancellationTokenCore(task, cts).Forget();
  15. return cts.Token;
  16. }
  17. public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
  18. {
  19. if (linkToken.IsCancellationRequested)
  20. {
  21. return linkToken;
  22. }
  23. if (!linkToken.CanBeCanceled)
  24. {
  25. return ToCancellationToken(task);
  26. }
  27. var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
  28. ToCancellationTokenCore(task, cts).Forget();
  29. return cts.Token;
  30. }
  31. public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
  32. {
  33. return ToCancellationToken(task.AsUniTask());
  34. }
  35. public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
  36. {
  37. return ToCancellationToken(task.AsUniTask(), linkToken);
  38. }
  39. static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
  40. {
  41. try
  42. {
  43. await task;
  44. }
  45. catch (Exception ex)
  46. {
  47. UniTaskScheduler.PublishUnobservedTaskException(ex);
  48. }
  49. cts.Cancel();
  50. cts.Dispose();
  51. }
  52. public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
  53. {
  54. if (cancellationToken.IsCancellationRequested)
  55. {
  56. return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
  57. }
  58. var promise = new UniTaskCompletionSource();
  59. return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
  60. }
  61. static void Callback(object state)
  62. {
  63. var promise = (UniTaskCompletionSource)state;
  64. promise.TrySetResult();
  65. }
  66. public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
  67. {
  68. return new CancellationTokenAwaitable(cancellationToken);
  69. }
  70. public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
  71. {
  72. var restoreFlow = false;
  73. if (!ExecutionContext.IsFlowSuppressed())
  74. {
  75. ExecutionContext.SuppressFlow();
  76. restoreFlow = true;
  77. }
  78. try
  79. {
  80. return cancellationToken.Register(callback, false);
  81. }
  82. finally
  83. {
  84. if (restoreFlow)
  85. {
  86. ExecutionContext.RestoreFlow();
  87. }
  88. }
  89. }
  90. public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state)
  91. {
  92. var restoreFlow = false;
  93. if (!ExecutionContext.IsFlowSuppressed())
  94. {
  95. ExecutionContext.SuppressFlow();
  96. restoreFlow = true;
  97. }
  98. try
  99. {
  100. return cancellationToken.Register(callback, state, false);
  101. }
  102. finally
  103. {
  104. if (restoreFlow)
  105. {
  106. ExecutionContext.RestoreFlow();
  107. }
  108. }
  109. }
  110. public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
  111. {
  112. return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
  113. }
  114. static void DisposeCallback(object state)
  115. {
  116. var d = (IDisposable)state;
  117. d.Dispose();
  118. }
  119. }
  120. public struct CancellationTokenAwaitable
  121. {
  122. CancellationToken cancellationToken;
  123. public CancellationTokenAwaitable(CancellationToken cancellationToken)
  124. {
  125. this.cancellationToken = cancellationToken;
  126. }
  127. public Awaiter GetAwaiter()
  128. {
  129. return new Awaiter(cancellationToken);
  130. }
  131. public struct Awaiter : ICriticalNotifyCompletion
  132. {
  133. CancellationToken cancellationToken;
  134. public Awaiter(CancellationToken cancellationToken)
  135. {
  136. this.cancellationToken = cancellationToken;
  137. }
  138. public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested;
  139. public void GetResult()
  140. {
  141. }
  142. public void OnCompleted(Action continuation)
  143. {
  144. UnsafeOnCompleted(continuation);
  145. }
  146. public void UnsafeOnCompleted(Action continuation)
  147. {
  148. cancellationToken.RegisterWithoutCaptureExecutionContext(continuation);
  149. }
  150. }
  151. }
  152. }