您不能選擇超過 %s 個話題 話題必須以字母或數字為開頭,可包含連接號 ('-') 且最長為 35 個字
 
 
 

185 行
5.5 KiB

  1. using Cysharp.Threading.Tasks.Internal;
  2. using System;
  3. using System.Threading;
  4. namespace Cysharp.Threading.Tasks.Linq
  5. {
  6. public static partial class UniTaskAsyncEnumerable
  7. {
  8. public static IUniTaskAsyncEnumerable<T> Create<T>(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
  9. {
  10. Error.ThrowArgumentNullException(create, nameof(create));
  11. return new Create<T>(create);
  12. }
  13. }
  14. public interface IAsyncWriter<T>
  15. {
  16. UniTask YieldAsync(T value);
  17. }
  18. internal sealed class Create<T> : IUniTaskAsyncEnumerable<T>
  19. {
  20. readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
  21. public Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
  22. {
  23. this.create = create;
  24. }
  25. public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
  26. {
  27. return new _Create(create, cancellationToken);
  28. }
  29. sealed class _Create : MoveNextSource, IUniTaskAsyncEnumerator<T>
  30. {
  31. readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
  32. readonly CancellationToken cancellationToken;
  33. int state = -1;
  34. AsyncWriter writer;
  35. public _Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create, CancellationToken cancellationToken)
  36. {
  37. this.create = create;
  38. this.cancellationToken = cancellationToken;
  39. TaskTracker.TrackActiveTask(this, 3);
  40. }
  41. public T Current { get; private set; }
  42. public UniTask DisposeAsync()
  43. {
  44. TaskTracker.RemoveTracking(this);
  45. writer.Dispose();
  46. return default;
  47. }
  48. public UniTask<bool> MoveNextAsync()
  49. {
  50. if (state == -2) return default;
  51. completionSource.Reset();
  52. MoveNext();
  53. return new UniTask<bool>(this, completionSource.Version);
  54. }
  55. void MoveNext()
  56. {
  57. try
  58. {
  59. switch (state)
  60. {
  61. case -1: // init
  62. {
  63. writer = new AsyncWriter(this);
  64. RunWriterTask(create(writer, cancellationToken)).Forget();
  65. if (Volatile.Read(ref state) == -2)
  66. {
  67. return; // complete synchronously
  68. }
  69. state = 0; // wait YieldAsync, it set TrySetResult(true)
  70. return;
  71. }
  72. case 0:
  73. writer.SignalWriter();
  74. return;
  75. default:
  76. goto DONE;
  77. }
  78. }
  79. catch (Exception ex)
  80. {
  81. state = -2;
  82. completionSource.TrySetException(ex);
  83. return;
  84. }
  85. DONE:
  86. state = -2;
  87. completionSource.TrySetResult(false);
  88. return;
  89. }
  90. async UniTaskVoid RunWriterTask(UniTask task)
  91. {
  92. try
  93. {
  94. await task;
  95. goto DONE;
  96. }
  97. catch (Exception ex)
  98. {
  99. Volatile.Write(ref state, -2);
  100. completionSource.TrySetException(ex);
  101. return;
  102. }
  103. DONE:
  104. Volatile.Write(ref state, -2);
  105. completionSource.TrySetResult(false);
  106. }
  107. public void SetResult(T value)
  108. {
  109. Current = value;
  110. completionSource.TrySetResult(true);
  111. }
  112. }
  113. sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable
  114. {
  115. readonly _Create enumerator;
  116. UniTaskCompletionSourceCore<AsyncUnit> core;
  117. public AsyncWriter(_Create enumerator)
  118. {
  119. this.enumerator = enumerator;
  120. }
  121. public void Dispose()
  122. {
  123. var status = core.GetStatus(core.Version);
  124. if (status == UniTaskStatus.Pending)
  125. {
  126. core.TrySetCanceled();
  127. }
  128. }
  129. public void GetResult(short token)
  130. {
  131. core.GetResult(token);
  132. }
  133. public UniTaskStatus GetStatus(short token)
  134. {
  135. return core.GetStatus(token);
  136. }
  137. public UniTaskStatus UnsafeGetStatus()
  138. {
  139. return core.UnsafeGetStatus();
  140. }
  141. public void OnCompleted(Action<object> continuation, object state, short token)
  142. {
  143. core.OnCompleted(continuation, state, token);
  144. }
  145. public UniTask YieldAsync(T value)
  146. {
  147. core.Reset();
  148. enumerator.SetResult(value);
  149. return new UniTask(this, core.Version);
  150. }
  151. public void SignalWriter()
  152. {
  153. core.TrySetResult(AsyncUnit.Default);
  154. }
  155. }
  156. }
  157. }