static IObservable <T> ConcatCore <T>(IEnumerable <IObservable <T> > sources) { return(Observable.Create <T>(observer => { var isDisposed = false; var e = sources.AsSafeEnumerable().GetEnumerator(); var subscription = new SerialDisposable(); var gate = new object(); var schedule = Scheduler.DefaultSchedulers.TailRecursion.Schedule(self => { lock (gate) { if (isDisposed) { return; } var current = default(IObservable <T>); var hasNext = false; var ex = default(Exception); try { hasNext = e.MoveNext(); if (hasNext) { current = e.Current; if (current == null) { throw new InvalidOperationException("sequence is null."); } } else { e.Dispose(); } } catch (Exception exception) { ex = exception; e.Dispose(); } if (ex != null) { observer.OnError(ex); return; } if (!hasNext) { observer.OnCompleted(); return; } var source = e.Current; var d = new SingleAssignmentDisposable(); subscription.Disposable = d; d.Disposable = source.Subscribe(observer.OnNext, observer.OnError, self); // OnCompleted, run self } }); return new CompositeDisposable(schedule, subscription, Disposable.Create(() => { lock (gate) { isDisposed = true; e.Dispose(); } })); })); }
public static IObservable <TResult> Zip <TLeft, TRight, TResult>(this IObservable <TLeft> left, IObservable <TRight> right, Func <TLeft, TRight, TResult> selector) { return(Observable.Create <TResult>(observer => { var gate = new object(); var leftQ = new Queue <TLeft>(); bool leftCompleted = false; var rightQ = new Queue <TRight>(); var rightCompleted = false; Action dequeue = () => { TLeft lv; TRight rv; TResult v; if (leftQ.Count != 0 && rightQ.Count != 0) { lv = leftQ.Dequeue(); rv = rightQ.Dequeue(); } else if (leftCompleted || rightCompleted) { observer.OnCompleted(); return; } else { return; } try { v = selector(lv, rv); } catch (Exception ex) { observer.OnError(ex); return; } observer.OnNext(v); }; var lsubscription = left.Synchronize(gate).Subscribe(x => { leftQ.Enqueue(x); dequeue(); }, observer.OnError, () => { leftCompleted = true; if (rightCompleted) { observer.OnCompleted(); } }); var rsubscription = right.Synchronize(gate).Subscribe(x => { rightQ.Enqueue(x); dequeue(); }, observer.OnError, () => { rightCompleted = true; if (leftCompleted) { observer.OnCompleted(); } }); return new CompositeDisposable { lsubscription, rsubscription, Disposable.Create(() => { lock (gate) { leftQ.Clear(); rightQ.Clear(); } }) }; })); }
public static IObservable <IList <T> > Zip <T>(params IObservable <T>[] sources) { return(Observable.Create <IList <T> >(observer => { var gate = new object(); var length = sources.Length; var queues = new Queue <T> [length]; for (int i = 0; i < length; i++) { queues[i] = new Queue <T>(); } var isDone = new bool[length]; Action <int> dequeue = index => { lock (gate) { if (queues.All(x => x.Count > 0)) { var result = queues.Select(x => x.Dequeue()).ToList(); observer.OnNext(result); return; } if (isDone.Where((x, i) => i != index).All(x => x)) { observer.OnCompleted(); return; } } }; var subscriptions = sources .Select((source, index) => { var d = new SingleAssignmentDisposable(); d.Disposable = source.Subscribe(x => { lock (gate) { queues[index].Enqueue(x); dequeue(index); } }, ex => { lock (gate) { observer.OnError(ex); } }, () => { lock (gate) { isDone[index] = true; if (isDone.All(x => x)) { observer.OnCompleted(); } else { d.Dispose(); } } }); return d; }) .ToArray(); return new CompositeDisposable(subscriptions) { Disposable.Create(() => { lock (gate) { foreach (var item in queues) { item.Clear(); } } }) }; })); }
public static IObservable <TSource> Catch <TSource>(this IEnumerable <IObservable <TSource> > sources) { // this code is borrowed from RxOfficial(rx.codeplex.com) and modified return(Observable.Create <TSource>(observer => { var gate = new object(); var isDisposed = false; var e = sources.GetEnumerator(); var subscription = new SerialDisposable(); var lastException = default(Exception); var cancelable = Scheduler.CurrentThread.Schedule(self => { lock (gate) { var current = default(IObservable <TSource>); var hasNext = false; var ex = default(Exception); if (!isDisposed) { try { hasNext = e.MoveNext(); if (hasNext) { current = e.Current; } else { e.Dispose(); } } catch (Exception exception) { ex = exception; e.Dispose(); } } else { return; } if (ex != null) { observer.OnError(ex); return; } if (!hasNext) { if (lastException != null) { observer.OnError(lastException); } else { observer.OnCompleted(); } return; } var d = new SingleAssignmentDisposable(); subscription.Disposable = d; d.Disposable = current.Subscribe(observer.OnNext, exception => { lastException = exception; self(); }, observer.OnCompleted); } }); return new CompositeDisposable(subscription, cancelable, Disposable.Create(() => { lock (gate) { e.Dispose(); isDisposed = true; } })); })); }