PageRenderTime 66ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Rx 1.0.10605/Source/Rxx/System/Reactive/Linq/Observable2 - Introspection.cs

#
C# | 343 lines | 233 code | 61 blank | 49 comment | 33 complexity | 6086cf3b7b53c2ed5e932118792c45ca MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.Diagnostics.Contracts;
  3. using System.Linq;
  4. using System.Reactive.Concurrency;
  5. using System.Reactive.Disposables;
  6. using System.Reactive.Subjects;
  7. namespace System.Reactive.Linq
  8. {
  9. public static partial class Observable2
  10. {
  11. /// <summary>
  12. /// Pairs the specified observable sequence with an observable for each value that indicates
  13. /// the duration of the observation of that value.
  14. /// </summary>
  15. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  16. /// <param name="source">The observable sequence to introspect.</param>
  17. /// <returns>A paired observable with the left channel providing introspection windows and the
  18. /// right channel providing values from the specified observable.</returns>
  19. public static IPairedObservable<IObservable<TSource>, TSource> Introspect<TSource>(
  20. this IObservable<TSource> source)
  21. {
  22. Contract.Requires(source != null);
  23. Contract.Ensures(Contract.Result<IPairedObservable<IObservable<TSource>, TSource>>() != null);
  24. var scheduler = Scheduler.Immediate;
  25. Contract.Assume(scheduler != null);
  26. return Introspect(source, scheduler);
  27. }
  28. /// <summary>
  29. /// Pairs the specified observable sequence with an observable for each value that indicates
  30. /// the duration of the observation of that value.
  31. /// </summary>
  32. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  33. /// <param name="source">The observable sequence to introspect.</param>
  34. /// <param name="scheduler">Schedules the observations of values in the right notification channel.</param>
  35. /// <returns>A paired observable with the left channel providing introspection windows and the
  36. /// right channel providing values from the specified observable.</returns>
  37. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
  38. Justification = "The introspection subject does not need to be disposed.")]
  39. public static IPairedObservable<IObservable<TSource>, TSource> Introspect<TSource>(
  40. this IObservable<TSource> source,
  41. IScheduler scheduler)
  42. {
  43. Contract.Requires(source != null);
  44. Contract.Requires(scheduler != null);
  45. Contract.Ensures(Contract.Result<IPairedObservable<IObservable<TSource>, TSource>>() != null);
  46. return PairedObservable.Create<IObservable<TSource>, TSource>(
  47. observer =>
  48. {
  49. var subject = new Subject<Tuple<TSource, ISubject<TSource>>>();
  50. var observations = Subject.Synchronize(subject, scheduler);
  51. int pendingOnNext = 0;
  52. bool sourceCompleted = false;
  53. object gate = new object();
  54. var observationsSubscription = observations.Subscribe(
  55. next =>
  56. {
  57. var value = next.Item1;
  58. var introspection = next.Item2;
  59. try
  60. {
  61. observer.OnNextRight(value);
  62. }
  63. catch (Exception ex)
  64. {
  65. introspection.OnError(ex);
  66. return;
  67. }
  68. introspection.OnCompleted();
  69. bool completeNow = false;
  70. lock (gate)
  71. {
  72. if (--pendingOnNext == 0 && sourceCompleted)
  73. {
  74. completeNow = true;
  75. }
  76. }
  77. if (completeNow)
  78. {
  79. observer.OnCompleted();
  80. }
  81. },
  82. observer.OnError,
  83. observer.OnCompleted);
  84. var sourceSubscription = source.Subscribe(
  85. value =>
  86. {
  87. var introspection = new ReplaySubject<TSource>(1);
  88. observer.OnNextLeft(introspection.AsObservable());
  89. introspection.OnNext(value);
  90. lock (gate)
  91. {
  92. pendingOnNext++;
  93. }
  94. observations.OnNext(Tuple.Create(value, (ISubject<TSource>) introspection));
  95. },
  96. observations.OnError,
  97. () =>
  98. {
  99. bool completeNow = false;
  100. lock (gate)
  101. {
  102. sourceCompleted = true;
  103. completeNow = pendingOnNext == 0;
  104. }
  105. if (completeNow)
  106. {
  107. observations.OnCompleted();
  108. }
  109. });
  110. return new CompositeDisposable(sourceSubscription, observationsSubscription, subject);
  111. });
  112. }
  113. /// <summary>
  114. /// Generates a sequence of windows where each window contains all values that were observed from
  115. /// the <paramref name="source"/> while the values in the previous window were being observed.
  116. /// </summary>
  117. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  118. /// <param name="source">The observable sequence from which to create introspection windows.</param>
  119. /// <returns>The source observable sequence buffered into introspection windows.</returns>
  120. public static IObservable<IObservable<TSource>> WindowIntrospective<TSource>(
  121. this IObservable<TSource> source)
  122. {
  123. Contract.Requires(source != null);
  124. Contract.Ensures(Contract.Result<IObservable<IObservable<TSource>>>() != null);
  125. #if !SILVERLIGHT
  126. var scheduler = Scheduler.TaskPool;
  127. #else
  128. var scheduler = Scheduler.ThreadPool;
  129. #endif
  130. Contract.Assume(scheduler != null);
  131. return WindowIntrospective(source, scheduler);
  132. }
  133. /// <summary>
  134. /// Generates a sequence of windows where each window contains all values that were observed from
  135. /// the <paramref name="source"/> while the values in the previous window were being observed.
  136. /// </summary>
  137. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  138. /// <param name="source">The observable sequence from which to create introspection windows.</param>
  139. /// <param name="scheduler">Schedules when windows are observed as well as the values in each window.</param>
  140. /// <returns>The source observable sequence buffered into introspection windows.</returns>
  141. public static IObservable<IObservable<TSource>> WindowIntrospective<TSource>(
  142. this IObservable<TSource> source,
  143. IScheduler scheduler)
  144. {
  145. Contract.Requires(source != null);
  146. Contract.Requires(scheduler != null);
  147. Contract.Ensures(Contract.Result<IObservable<IObservable<TSource>>>() != null);
  148. var observable = Observable.Create<IObservable<TSource>>(
  149. observer =>
  150. {
  151. var queue = new Queue<TSource>();
  152. var window = new Subject<TSource>();
  153. bool pendingDrain = false;
  154. bool sourceCompleted = false;
  155. object gate = new object();
  156. var sourceSubscription = new SingleAssignmentDisposable();
  157. var drainSchedule = new SerialDisposable();
  158. var schedules = new CompositeDisposable(drainSchedule);
  159. Action ensureDraining = () =>
  160. {
  161. if (pendingDrain)
  162. return;
  163. pendingDrain = true;
  164. drainSchedule.Disposable =
  165. scheduler.Schedule(self =>
  166. {
  167. Queue<TSource> currentQueue;
  168. lock (gate)
  169. {
  170. currentQueue = queue;
  171. queue = new Queue<TSource>();
  172. }
  173. currentQueue.ForEach(window.OnNext);
  174. window.OnCompleted();
  175. Contract.Assume(pendingDrain);
  176. bool loop, completeNow;
  177. lock (gate)
  178. {
  179. pendingDrain = queue.Count > 0;
  180. completeNow = !pendingDrain && sourceCompleted;
  181. if (completeNow)
  182. {
  183. loop = false;
  184. }
  185. else
  186. {
  187. window = new Subject<TSource>();
  188. // Must push the new window before unlocking the gate to avoid a race condition when pendingDrain is false.
  189. observer.OnNext(window);
  190. // Ensure pendingDrain is read again after making a call to OnNext, in case of re-entry.
  191. loop = pendingDrain;
  192. }
  193. }
  194. if (completeNow)
  195. {
  196. observer.OnCompleted();
  197. }
  198. else if (loop)
  199. {
  200. Contract.Assert(pendingDrain);
  201. self();
  202. }
  203. });
  204. };
  205. schedules.Add(
  206. scheduler.Schedule(() =>
  207. {
  208. observer.OnNext(window);
  209. sourceSubscription.Disposable = source.Subscribe(
  210. value =>
  211. {
  212. lock (gate)
  213. {
  214. queue.Enqueue(value);
  215. ensureDraining();
  216. }
  217. },
  218. ex => schedules.Add(scheduler.Schedule(() => observer.OnError(ex))),
  219. () =>
  220. {
  221. bool completeNow = false;
  222. lock (gate)
  223. {
  224. sourceCompleted = true;
  225. completeNow = !pendingDrain;
  226. }
  227. if (completeNow)
  228. {
  229. schedules.Add(scheduler.Schedule(() =>
  230. {
  231. window.OnCompleted();
  232. observer.OnCompleted();
  233. }));
  234. }
  235. });
  236. }));
  237. return new CompositeDisposable(sourceSubscription, drainSchedule, schedules);
  238. });
  239. Contract.Assume(observable != null);
  240. return observable;
  241. }
  242. /// <summary>
  243. /// Generates a sequence of lists where each list contains all values that were observed from
  244. /// the <paramref name="source"/> while the previous list was being observed.
  245. /// </summary>
  246. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  247. /// <param name="source">The observable sequence from which to create introspection lists.</param>
  248. /// <returns>The source observable sequence buffered into introspection lists.</returns>
  249. public static IObservable<IList<TSource>> BufferIntrospective<TSource>(
  250. this IObservable<TSource> source)
  251. {
  252. Contract.Requires(source != null);
  253. Contract.Ensures(Contract.Result<IObservable<IList<TSource>>>() != null);
  254. var observable = from window in source.WindowIntrospective()
  255. from list in window.ToList()
  256. select list;
  257. Contract.Assume(observable != null);
  258. return observable;
  259. }
  260. /// <summary>
  261. /// Generates a sequence of lists where each list contains all values that were observed from
  262. /// the <paramref name="source"/> while the previous list was being observed.
  263. /// </summary>
  264. /// <typeparam name="TSource">The object that provides notification information.</typeparam>
  265. /// <param name="source">The observable sequence from which to create introspection lists.</param>
  266. /// <param name="scheduler">Schedules when lists are observed.</param>
  267. /// <returns>The source observable sequence buffered into introspection lists.</returns>
  268. public static IObservable<IList<TSource>> BufferIntrospective<TSource>(
  269. this IObservable<TSource> source,
  270. IScheduler scheduler)
  271. {
  272. Contract.Requires(source != null);
  273. Contract.Requires(scheduler != null);
  274. Contract.Ensures(Contract.Result<IObservable<IList<TSource>>>() != null);
  275. var observable = from window in source.WindowIntrospective(scheduler)
  276. from list in window.ToList()
  277. select list;
  278. Contract.Assume(observable != null);
  279. return observable;
  280. }
  281. }
  282. }