Amb.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reactive.Disposables;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace System.Reactive.Linq
  10. {
  11. partial class AsyncObservable
  12. {
  13. public static IAsyncObservable<TSource> Amb<TSource>(this IAsyncObservable<TSource> first, IAsyncObservable<TSource> second)
  14. {
  15. if (first == null)
  16. throw new ArgumentNullException(nameof(first));
  17. if (second == null)
  18. throw new ArgumentNullException(nameof(second));
  19. return Create<TSource>(async observer =>
  20. {
  21. var firstSubscription = new SingleAssignmentAsyncDisposable();
  22. var secondSubscription = new SingleAssignmentAsyncDisposable();
  23. var (firstObserver, secondObserver) = AsyncObserver.Amb(observer, firstSubscription, secondSubscription);
  24. var firstTask = first.SubscribeSafeAsync(firstObserver).ContinueWith(d => firstSubscription.AssignAsync(d.Result)).Unwrap();
  25. var secondTask = second.SubscribeSafeAsync(secondObserver).ContinueWith(d => secondSubscription.AssignAsync(d.Result)).Unwrap();
  26. await Task.WhenAll(firstTask, secondTask).ConfigureAwait(false);
  27. return StableCompositeAsyncDisposable.Create(firstSubscription, secondSubscription);
  28. });
  29. }
  30. public static IAsyncObservable<TSource> Amb<TSource>(this IEnumerable<IAsyncObservable<TSource>> sources) => Amb(sources.ToArray());
  31. public static IAsyncObservable<TSource> Amb<TSource>(params IAsyncObservable<TSource>[] sources)
  32. {
  33. if (sources == null)
  34. throw new ArgumentNullException(nameof(sources));
  35. return Create<TSource>(async observer =>
  36. {
  37. var count = sources.Length;
  38. var subscriptions = new SingleAssignmentAsyncDisposable[count];
  39. for (var i = 0; i < count; i++)
  40. {
  41. subscriptions[i] = new SingleAssignmentAsyncDisposable();
  42. }
  43. var observers = AsyncObserver.Amb(observer, subscriptions);
  44. var tasks = new Task[count];
  45. for (var i = 0; i < count; i++)
  46. {
  47. tasks[i] = sources[i].SubscribeSafeAsync(observers[i]).ContinueWith(d => subscriptions[i].AssignAsync(d.Result)).Unwrap();
  48. }
  49. await Task.WhenAll(tasks).ConfigureAwait(false);
  50. return StableCompositeAsyncDisposable.Create(subscriptions);
  51. });
  52. }
  53. }
  54. partial class AsyncObserver
  55. {
  56. public static (IAsyncObserver<TSource>, IAsyncObserver<TSource>) Amb<TSource>(IAsyncObserver<TSource> observer, IAsyncDisposable first, IAsyncDisposable second)
  57. {
  58. if (observer == null)
  59. throw new ArgumentNullException(nameof(observer));
  60. if (first == null)
  61. throw new ArgumentNullException(nameof(first));
  62. if (second == null)
  63. throw new ArgumentNullException(nameof(second));
  64. var gate = new AsyncLock();
  65. var state = AmbState.None;
  66. return
  67. (
  68. Create<TSource>(
  69. async x =>
  70. {
  71. using (await gate.LockAsync().ConfigureAwait(false))
  72. {
  73. if (state == AmbState.None)
  74. {
  75. state = AmbState.First;
  76. await second.DisposeAsync().ConfigureAwait(false);
  77. }
  78. if (state == AmbState.First)
  79. {
  80. await observer.OnNextAsync(x).ConfigureAwait(false);
  81. }
  82. }
  83. },
  84. async ex =>
  85. {
  86. using (await gate.LockAsync().ConfigureAwait(false))
  87. {
  88. if (state == AmbState.None)
  89. {
  90. state = AmbState.First;
  91. await second.DisposeAsync().ConfigureAwait(false);
  92. }
  93. if (state == AmbState.First)
  94. {
  95. await observer.OnErrorAsync(ex).ConfigureAwait(false);
  96. }
  97. }
  98. },
  99. async () =>
  100. {
  101. using (await gate.LockAsync().ConfigureAwait(false))
  102. {
  103. if (state == AmbState.None)
  104. {
  105. state = AmbState.First;
  106. await second.DisposeAsync().ConfigureAwait(false);
  107. }
  108. if (state == AmbState.First)
  109. {
  110. await observer.OnCompletedAsync().ConfigureAwait(false);
  111. }
  112. }
  113. }
  114. ),
  115. Create<TSource>(
  116. async x =>
  117. {
  118. using (await gate.LockAsync().ConfigureAwait(false))
  119. {
  120. if (state == AmbState.None)
  121. {
  122. state = AmbState.Second;
  123. await first.DisposeAsync().ConfigureAwait(false);
  124. }
  125. if (state == AmbState.Second)
  126. {
  127. await observer.OnNextAsync(x).ConfigureAwait(false);
  128. }
  129. }
  130. },
  131. async ex =>
  132. {
  133. using (await gate.LockAsync().ConfigureAwait(false))
  134. {
  135. if (state == AmbState.None)
  136. {
  137. state = AmbState.Second;
  138. await first.DisposeAsync().ConfigureAwait(false);
  139. }
  140. if (state == AmbState.Second)
  141. {
  142. await observer.OnErrorAsync(ex).ConfigureAwait(false);
  143. }
  144. }
  145. },
  146. async () =>
  147. {
  148. using (await gate.LockAsync().ConfigureAwait(false))
  149. {
  150. if (state == AmbState.None)
  151. {
  152. state = AmbState.Second;
  153. await first.DisposeAsync().ConfigureAwait(false);
  154. }
  155. if (state == AmbState.Second)
  156. {
  157. await observer.OnCompletedAsync().ConfigureAwait(false);
  158. }
  159. }
  160. }
  161. )
  162. );
  163. }
  164. public static IAsyncObserver<TSource>[] Amb<TSource>(IAsyncObserver<TSource> observer, IAsyncDisposable[] subscriptions)
  165. {
  166. if (observer == null)
  167. throw new ArgumentNullException(nameof(observer));
  168. if (subscriptions == null)
  169. throw new ArgumentNullException(nameof(subscriptions));
  170. var gate = new AsyncLock();
  171. var winner = default(int?);
  172. var count = subscriptions.Length;
  173. async Task ElectWinnerAsync(int index)
  174. {
  175. winner = index;
  176. var dispose = new List<Task>(count - 1);
  177. for (var i = 0; i < count; i++)
  178. {
  179. if (i != index)
  180. {
  181. dispose.Add(subscriptions[i].DisposeAsync());
  182. }
  183. }
  184. await Task.WhenAll(dispose).ConfigureAwait(false);
  185. }
  186. IAsyncObserver<TSource> CreateObserver(int index) =>
  187. Create<TSource>(
  188. async x =>
  189. {
  190. using (await gate.LockAsync().ConfigureAwait(false))
  191. {
  192. if (winner == null)
  193. {
  194. await ElectWinnerAsync(index).ConfigureAwait(false);
  195. }
  196. if (winner == index)
  197. {
  198. await observer.OnNextAsync(x).ConfigureAwait(false);
  199. }
  200. }
  201. },
  202. async ex =>
  203. {
  204. using (await gate.LockAsync().ConfigureAwait(false))
  205. {
  206. if (winner == null)
  207. {
  208. await ElectWinnerAsync(index).ConfigureAwait(false);
  209. }
  210. if (winner == index)
  211. {
  212. await observer.OnErrorAsync(ex).ConfigureAwait(false);
  213. }
  214. }
  215. },
  216. async () =>
  217. {
  218. using (await gate.LockAsync().ConfigureAwait(false))
  219. {
  220. if (winner == null)
  221. {
  222. await ElectWinnerAsync(index).ConfigureAwait(false);
  223. }
  224. if (winner == index)
  225. {
  226. await observer.OnCompletedAsync().ConfigureAwait(false);
  227. }
  228. }
  229. }
  230. );
  231. var res = new IAsyncObserver<TSource>[count];
  232. for (var i = 0; i < count; i++)
  233. {
  234. res[i] = CreateObserver(i);
  235. }
  236. return res;
  237. }
  238. private enum AmbState
  239. {
  240. None,
  241. First,
  242. Second,
  243. }
  244. }
  245. }