public static IObservable <TSource> Delay <TSource>(this IObservable <TSource> source, TimeSpan dueTime, IScheduler scheduler) { // This code is borrowed from Rx(rx.codeplex.com) return(Observable.Create <TSource>(observer => { var gate = new object(); var q = new Queue <Timestamped <Notification <TSource> > >(); var active = false; var running = false; var cancelable = new SerialDisposable(); var exception = default(Exception); var subscription = source.Materialize().Timestamp(scheduler).Subscribe(notification => { var shouldRun = false; lock (gate) { if (notification.Value.Kind == NotificationKind.OnError) { q.Clear(); q.Enqueue(notification); exception = notification.Value.Exception; shouldRun = !running; } else { q.Enqueue(new Timestamped <Notification <TSource> >(notification.Value, notification.Timestamp.Add(dueTime))); shouldRun = !active; active = true; } } if (shouldRun) { if (exception != null) { observer.OnError(exception); } else { var d = new SingleAssignmentDisposable(); cancelable.Disposable = d; d.Disposable = scheduler.Schedule(dueTime, self => { lock (gate) { if (exception != null) { return; } running = true; } Notification <TSource> result; do { result = null; lock (gate) { if (q.Count > 0 && q.Peek().Timestamp.CompareTo(scheduler.Now) <= 0) { result = q.Dequeue().Value; } } if (result != null) { result.Accept(observer); } } while (result != null); var shouldRecurse = false; var recurseDueTime = TimeSpan.Zero; var e = default(Exception); lock (gate) { if (q.Count > 0) { shouldRecurse = true; recurseDueTime = TimeSpan.FromTicks(Math.Max(0, q.Peek().Timestamp.Subtract(scheduler.Now).Ticks)); } else { active = false; } e = exception; running = false; } if (e != null) { observer.OnError(e); } else if (shouldRecurse) { self(recurseDueTime); } }); } } }); return new CompositeDisposable(subscription, cancelable); })); }
public static IObservable <TSource> Throttle <TSource>(this IObservable <TSource> source, TimeSpan dueTime, IScheduler scheduler) { // this code is borrowed from Rx Official(rx.codeplex.com) return(new AnonymousObservable <TSource>(observer => { var gate = new object(); var value = default(TSource); var hasValue = false; var cancelable = new SerialDisposable(); var id = 0UL; var subscription = source.Subscribe(x => { ulong currentid; lock (gate) { hasValue = true; value = x; id = unchecked (id + 1); currentid = id; } var d = new SingleAssignmentDisposable(); cancelable.Disposable = d; d.Disposable = scheduler.Schedule(dueTime, () => { lock (gate) { if (hasValue && id == currentid) { observer.OnNext(value); } hasValue = false; } }); }, exception => { cancelable.Dispose(); lock (gate) { observer.OnError(exception); hasValue = false; id = unchecked (id + 1); } }, () => { cancelable.Dispose(); lock (gate) { if (hasValue) { observer.OnNext(value); } observer.OnCompleted(); hasValue = false; id = unchecked (id + 1); } }); return new CompositeDisposable(subscription, cancelable); })); }
public static IObservable <IList <T> > Buffer <T>(this IObservable <T> source, TimeSpan timeSpan, int count, IScheduler scheduler) { if (source == null) { throw new ArgumentNullException("source"); } if (count <= 0) { throw new ArgumentOutOfRangeException("count <= 0"); } return(Observable.Create <IList <T> >(observer => { var list = new List <T>(); var gate = new object(); var timerId = 0L; var d = new CompositeDisposable(2); var timerD = new SerialDisposable(); // timer d.Add(timerD); Action createTimer = () => { var currentTimerId = timerId; var timerS = new SingleAssignmentDisposable(); timerD.Disposable = timerS; // restart timer(dispose before) timerS.Disposable = scheduler.Schedule(timeSpan, self => { List <T> currentList; lock (gate) { if (currentTimerId != timerId) { return; } currentList = list; if (currentList.Count != 0) { list = new List <T>(); } } if (currentList.Count != 0) { observer.OnNext(currentList); } self(timeSpan); }); }; createTimer(); // subscription d.Add(source.Subscribe(x => { List <T> currentList = null; lock (gate) { list.Add(x); if (list.Count == count) { currentList = list; list = new List <T>(); timerId++; createTimer(); } } if (currentList != null) { observer.OnNext(currentList); } }, observer.OnError, () => { lock (gate) { timerId++; } var currentList = list; observer.OnNext(currentList); observer.OnCompleted(); })); return d; })); }
public static IObservable <IList <T> > Buffer <T>(this IObservable <T> source, TimeSpan timeSpan, TimeSpan timeShift, IScheduler scheduler) { if (source == null) { throw new ArgumentNullException("source"); } return(Observable.Create <IList <T> >(observer => { var totalTime = TimeSpan.Zero; var nextShift = timeShift; var nextSpan = timeSpan; var gate = new object(); var q = new Queue <IList <T> >(); var timerD = new SerialDisposable(); var createTimer = default(Action); createTimer = () => { var m = new SingleAssignmentDisposable(); timerD.Disposable = m; var isSpan = false; var isShift = false; if (nextSpan == nextShift) { isSpan = true; isShift = true; } else if (nextSpan < nextShift) { isSpan = true; } else { isShift = true; } var newTotalTime = isSpan ? nextSpan : nextShift; var ts = newTotalTime - totalTime; totalTime = newTotalTime; if (isSpan) { nextSpan += timeShift; } if (isShift) { nextShift += timeShift; } m.Disposable = scheduler.Schedule(ts, () => { lock (gate) { if (isShift) { var s = new List <T>(); q.Enqueue(s); } if (isSpan) { var s = q.Dequeue(); observer.OnNext(s); } } createTimer(); }); }; q.Enqueue(new List <T>()); createTimer(); return source.Subscribe( x => { lock (gate) { foreach (var s in q) { s.Add(x); } } }, observer.OnError, () => { lock (gate) { foreach (var list in q) { observer.OnNext(list); } observer.OnCompleted(); } } ); })); }
public static IObservable <TSource> ThrottleFrame <TSource>(this IObservable <TSource> source, int frameCount, FrameCountType frameCountType = FrameCountType.Update) { return(new Observable.AnonymousObservable <TSource>(observer => { var gate = new object(); var value = default(TSource); var hasValue = false; var cancelable = new SerialDisposable(); var id = 0UL; var subscription = source.Subscribe(x => { ulong currentid; lock (gate) { hasValue = true; value = x; id = unchecked (id + 1); currentid = id; } var d = new SingleAssignmentDisposable(); cancelable.Disposable = d; d.Disposable = IntervalFrame(frameCount, frameCountType) .Subscribe(_ => { lock (gate) { if (hasValue && id == currentid) { observer.OnNext(value); } hasValue = false; } }); }, exception => { cancelable.Dispose(); lock (gate) { observer.OnError(exception); hasValue = false; id = unchecked (id + 1); } }, () => { cancelable.Dispose(); lock (gate) { if (hasValue) { observer.OnNext(value); } observer.OnCompleted(); hasValue = false; id = unchecked (id + 1); } }); return new CompositeDisposable(subscription, cancelable); })); }
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.AsSafeEnumerable().GetEnumerator(); var subscription = new SerialDisposable(); var lastException = default(Exception); var cancelable = Scheduler.DefaultSchedulers.TailRecursion.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; } })); })); }
public static IObservable <T> Switch <T>(this IObservable <IObservable <T> > sources) { // this code is borrwed from RxOfficial(rx.codeplex.com) return(Observable.Create <T>(observer => { var gate = new object(); var innerSubscription = new SerialDisposable(); var isStopped = false; var latest = 0UL; var hasLatest = false; var subscription = sources.Subscribe( innerSource => { var id = default(ulong); lock (gate) { id = unchecked (++latest); hasLatest = true; } var d = new SingleAssignmentDisposable(); innerSubscription.Disposable = d; d.Disposable = innerSource.Subscribe( x => { lock (gate) { if (latest == id) { observer.OnNext(x); } } }, exception => { lock (gate) { if (latest == id) { observer.OnError(exception); } } }, () => { lock (gate) { if (latest == id) { hasLatest = false; if (isStopped) { observer.OnCompleted(); } } } }); }, exception => { lock (gate) observer.OnError(exception); }, () => { lock (gate) { isStopped = true; if (!hasLatest) { observer.OnCompleted(); } } }); return new CompositeDisposable(subscription, innerSubscription); })); }
/// <summary> /// <para>Specialized for single async operations like Task.WhenAll, Zip.Take(1).</para> /// <para>If sequence is empty, return T[0] array.</para> /// </summary> public static IObservable <T[]> WhenAll <T>(this IEnumerable <IObservable <T> > sources) { var array = sources as IObservable <T>[]; if (array != null) { return(WhenAll(array)); } return(Observable.Create <T[]>(observer => { var _sources = sources as IList <IObservable <T> >; if (_sources == null) { _sources = new List <IObservable <T> >(); foreach (var item in sources) { _sources.Add(item); } } var gate = new object(); var length = _sources.Count; var completedCount = 0; var values = new T[length]; if (length == 0) { observer.OnNext(values); observer.OnCompleted(); return Disposable.Empty; } var subscriptions = new IDisposable[length]; for (int index = 0; index < length; index++) { var source = _sources[index]; var capturedIndex = index; var d = new SingleAssignmentDisposable(); d.Disposable = source.Subscribe(x => { lock (gate) { values[capturedIndex] = x; } }, ex => { lock (gate) { observer.OnError(ex); } }, () => { lock (gate) { completedCount++; if (completedCount == length) { observer.OnNext(values); observer.OnCompleted(); } } }); subscriptions[index] = d; } return new CompositeDisposable(subscriptions); })); }
public static IObservable <IList <TSource> > CombineLatest <TSource>(params IObservable <TSource>[] sources) { // this code is borrwed from RxOfficial(rx.codeplex.com) return(Observable.Create <IList <TSource> >(observer => { var srcs = sources.ToArray(); var N = srcs.Length; var hasValue = new bool[N]; var hasValueAll = false; var values = new List <TSource>(N); for (int i = 0; i < N; i++) { values.Add(default(TSource)); } var isDone = new bool[N]; var next = new Action <int>(i => { hasValue[i] = true; if (hasValueAll || (hasValueAll = hasValue.All(x => x))) { var res = values.ToList(); observer.OnNext(res); } else if (isDone.Where((x, j) => j != i).All(x => x)) { observer.OnCompleted(); return; } }); var done = new Action <int>(i => { isDone[i] = true; if (isDone.All(x => x)) { observer.OnCompleted(); return; } }); var subscriptions = new SingleAssignmentDisposable[N]; var gate = new object(); for (int i = 0; i < N; i++) { var j = i; subscriptions[j] = new SingleAssignmentDisposable { Disposable = srcs[j].Synchronize(gate).Subscribe( x => { values[j] = x; next(j); }, observer.OnError, () => { done(j); } ) }; } return new CompositeDisposable(subscriptions); })); }
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 <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 <T> Merge <T>(this IObservable <IObservable <T> > sources, int maxConcurrent) { // this code is borrwed from RxOfficial(rx.codeplex.com) return(Observable.Create <T>(observer => { var gate = new object(); var q = new Queue <IObservable <T> >(); var isStopped = false; var group = new CompositeDisposable(); var activeCount = 0; var subscribe = default(Action <IObservable <T> >); subscribe = xs => { var subscription = new SingleAssignmentDisposable(); group.Add(subscription); subscription.Disposable = xs.Subscribe( x => { lock (gate) observer.OnNext(x); }, exception => { lock (gate) observer.OnError(exception); }, () => { group.Remove(subscription); lock (gate) { if (q.Count > 0) { var s = q.Dequeue(); subscribe(s); } else { activeCount--; if (isStopped && activeCount == 0) { observer.OnCompleted(); } } } }); }; group.Add(sources.Subscribe( innerSource => { lock (gate) { if (activeCount < maxConcurrent) { activeCount++; subscribe(innerSource); } else { q.Enqueue(innerSource); } } }, exception => { lock (gate) observer.OnError(exception); }, () => { lock (gate) { isStopped = true; if (activeCount == 0) { observer.OnCompleted(); } } })); return group; })); }
public static IObservable <T> Amb <T>(this IObservable <T> source, IObservable <T> second) { return(Observable.Create <T>(observer => { var choice = AmbState.Neither; var gate = new Object(); var leftSubscription = new SingleAssignmentDisposable(); var rightSubscription = new SingleAssignmentDisposable(); leftSubscription.Disposable = source.Subscribe(x => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Left; rightSubscription.Dispose(); // We can avoid lock every call but I'm not confident in AOT Safety. // I'll try, check... // left.Observer = observer; } } if (choice == AmbState.Left) { observer.OnNext(x); } }, ex => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Left; rightSubscription.Dispose(); } } if (choice == AmbState.Left) { observer.OnError(ex); } }, () => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Left; rightSubscription.Dispose(); } } if (choice == AmbState.Left) { observer.OnCompleted(); } }); rightSubscription.Disposable = second.Subscribe(x => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Right; leftSubscription.Dispose(); } } if (choice == AmbState.Right) { observer.OnNext(x); } }, ex => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Right; leftSubscription.Dispose(); } } if (choice == AmbState.Right) { observer.OnError(ex); } }, () => { lock (gate) { if (choice == AmbState.Neither) { choice = AmbState.Right; leftSubscription.Dispose(); } } if (choice == AmbState.Right) { observer.OnCompleted(); } }); return new CompositeDisposable { leftSubscription, rightSubscription }; })); }