public Timer(TimeSpan dueTime, Action action) { _disposable = new SingleAssignmentDisposable(); _disposable.Disposable = Disposable.Create(Unroot); _action = action; _timer = new System.Threading.Timer(Tick, null, dueTime, TimeSpan.FromMilliseconds(System.Threading.Timeout.Infinite)); lock (s_timers) { if (!_hasRemoved) { s_timers.Add(_timer); _hasAdded = true; } } }
public static IObservable <T> Catch <T, TException>(this IObservable <T> source, Func <TException, IObservable <T> > errorHandler) where TException : Exception { return(Observable.Create <T>(observer => { var serialDisposable = new SerialDisposable(); var rootDisposable = new SingleAssignmentDisposable(); serialDisposable.Disposable = rootDisposable; rootDisposable.Disposable = source.Subscribe(observer.OnNext, exception => { var e = exception as TException; if (e != null) { IObservable <T> next; try { next = errorHandler(e); } catch (Exception ex) { observer.OnError(ex); return; } var d = new SingleAssignmentDisposable(); serialDisposable.Disposable = d; d.Disposable = next.Subscribe(observer); } else { observer.OnError(exception); } }, observer.OnCompleted); return serialDisposable; })); }
public IDisposable Subscribe(IObserver <T> observer) { var subscription = new SingleAssignmentDisposable(); #if UNITY_5_0 // In Unity 5.0.2p1/p2 occures IL2CPP compile error. // IL2CPP compile error when script contains method group of interface to delegate conversion var safeObserver = Observer.Create <T>(x => observer.OnNext(x), ex => observer.OnError(ex), () => observer.OnCompleted(), subscription); #else var safeObserver = Observer.Create <T>(observer.OnNext, observer.OnError, observer.OnCompleted, subscription); #endif if (Scheduler.IsCurrentThreadSchedulerScheduleRequired) { Scheduler.CurrentThread.Schedule(() => subscription.Disposable = subscribe(safeObserver)); } else { subscription.Disposable = subscribe(safeObserver); } return(subscription); }
public static Task <T> WaitUntilValueChangedAsync <T>(this IReadOnlyReactiveProperty <T> source, CancellationToken cancellationToken = default(CancellationToken)) { var tcs = new CancellableTaskCompletionSource <T>(); var disposable = new SingleAssignmentDisposable(); if (source.HasValue) { // Skip first value var isFirstValue = true; disposable.Disposable = source.Subscribe(x => { if (isFirstValue) { isFirstValue = false; return; } else { disposable.Dispose(); // finish subscription. tcs.TrySetResult(x); } }, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled()); } else { disposable.Disposable = source.Subscribe(x => { disposable.Dispose(); // finish subscription. tcs.TrySetResult(x); }, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled()); } cancellationToken.Register(Callback, Tuple.Create(tcs, disposable.Disposable), false); return(tcs.Task); }
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); })); }
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 }; })); }
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; })); }
/// <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 void SingleAssignment() { SetScehdulerForImport(); var d = new SingleAssignmentDisposable(); d.IsDisposed.IsFalse(); var id1 = new IdDisp(1); var id2 = new IdDisp(2); var id3 = new IdDisp(3); // dispose first d.Dispose(); d.IsDisposed.IsTrue(); d.Disposable = id1; id1.IsDisposed.IsTrue(); d.Disposable = id2; id2.IsDisposed.IsTrue(); d.Disposable = id3; id3.IsDisposed.IsTrue(); // normal flow d = new SingleAssignmentDisposable(); id1 = new IdDisp(1); id2 = new IdDisp(2); id3 = new IdDisp(3); d.Disposable = id1; id1.IsDisposed.IsFalse(); d.Dispose(); id1.IsDisposed.IsTrue(); d.Disposable = id2; id2.IsDisposed.IsTrue(); d.Disposable = id3; id3.IsDisposed.IsTrue(); // exception flow d = new SingleAssignmentDisposable(); id1 = new IdDisp(1); id2 = new IdDisp(2); id3 = new IdDisp(3); d.Disposable = id1; AssertEx.Catch<InvalidOperationException>(() => d.Disposable = id2); // null d = new SingleAssignmentDisposable(); id1 = new IdDisp(1); d.Disposable = null; d.Dispose(); d.Disposable = null; UniRx.Scheduler.SetDefaultForUnity(); }
static IObservable <T> RepeatUntilCore <T>(this IEnumerable <IObservable <T> > sources, IObservable <Unit> trigger, GameObject lifeTimeChecker) { return(Observable.Create <T>(observer => { var isFirstSubscribe = true; var isDisposed = false; var isStopped = false; var e = sources.AsSafeEnumerable().GetEnumerator(); var subscription = new SerialDisposable(); var schedule = new SingleAssignmentDisposable(); var gate = new object(); var stopper = trigger.Subscribe(_ => { lock (gate) { isStopped = true; e.Dispose(); subscription.Dispose(); schedule.Dispose(); observer.OnCompleted(); } }, observer.OnError); schedule.Disposable = Scheduler.CurrentThread.Schedule(self => { lock (gate) { if (isDisposed) { return; } if (isStopped) { 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) { stopper.Dispose(); observer.OnError(ex); return; } if (!hasNext) { stopper.Dispose(); observer.OnCompleted(); return; } var source = e.Current; var d = new SingleAssignmentDisposable(); subscription.Disposable = d; var repeatObserver = Observer.Create <T>(observer.OnNext, observer.OnError, self); if (isFirstSubscribe) { isFirstSubscribe = false; d.Disposable = source.Subscribe(repeatObserver); } else { MainThreadDispatcher.SendStartCoroutine(SubscribeAfterEndOfFrame(d, source, repeatObserver, lifeTimeChecker)); } } }); return new CompositeDisposable(schedule, subscription, stopper, Disposable.Create(() => { lock (gate) { isDisposed = true; e.Dispose(); } })); })); }
public static IObservable <T> TimeoutFrame <T>(this IObservable <T> source, int frameCount, FrameCountType frameCountType = FrameCountType.Update) { return(Observable.Create <T>(observer => { object gate = new object(); var objectId = 0ul; var isTimeout = false; Func <ulong, IDisposable> runTimer = (timerId) => { return Observable.TimerFrame(frameCount, frameCountType) .Subscribe(_ => { lock (gate) { if (objectId == timerId) { isTimeout = true; } } if (isTimeout) { observer.OnError(new TimeoutException()); } }); }; var timerDisposable = new SerialDisposable(); timerDisposable.Disposable = runTimer(objectId); var sourceSubscription = new SingleAssignmentDisposable(); sourceSubscription.Disposable = source.Subscribe(x => { bool timeout; lock (gate) { timeout = isTimeout; objectId++; } if (timeout) { return; } timerDisposable.Disposable = Disposable.Empty; // cancel old timer observer.OnNext(x); timerDisposable.Disposable = runTimer(objectId); }, ex => { bool timeout; lock (gate) { timeout = isTimeout; objectId++; } if (timeout) { return; } timerDisposable.Dispose(); observer.OnError(ex); }, () => { bool timeout; lock (gate) { timeout = isTimeout; objectId++; } if (timeout) { return; } timerDisposable.Dispose(); observer.OnCompleted(); }); return new CompositeDisposable { timerDisposable, sourceSubscription }; })); }
public static IObservable <TSource> ThrottleFrame <TSource>(this IObservable <TSource> source, int frameCount, FrameCountType frameCountType = FrameCountType.Update) { 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 = Observable.TimerFrame(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 <T> SampleFrame <T>(this IObservable <T> source, int frameCount, FrameCountType frameCountType = FrameCountType.Update) { return(Observable.Create <T>(observer => { var latestValue = default(T); var isUpdated = false; var isCompleted = false; var gate = new object(); var scheduling = new SingleAssignmentDisposable(); scheduling.Disposable = Observable.IntervalFrame(frameCount, frameCountType) .Subscribe(_ => { lock (gate) { if (isUpdated) { var value = latestValue; isUpdated = false; try { observer.OnNext(value); } catch { scheduling.Dispose(); } } if (isCompleted) { observer.OnCompleted(); scheduling.Dispose(); } } }); var sourceSubscription = new SingleAssignmentDisposable(); sourceSubscription.Disposable = source.Subscribe(x => { lock (gate) { latestValue = x; isUpdated = true; } }, e => { lock (gate) { observer.OnError(e); scheduling.Dispose(); } } , () => { lock (gate) { isCompleted = true; sourceSubscription.Dispose(); } }); return new CompositeDisposable { scheduling, sourceSubscription }; })); }
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 <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> /// Returns a task that will receive the last value or the exception produced by the observable sequence. /// </summary> /// <typeparam name="TResult">The type of the elements in the source sequence.</typeparam> /// <param name="observable">Observable sequence to convert to a task.</param> /// <param name="cancellationToken">Cancellation token that can be used to cancel the task, causing unsubscription from the observable sequence.</param> /// <param name="state">The state to use as the underlying task's AsyncState.</param> /// <returns>A task that will receive the last element or the exception produced by the observable sequence.</returns> /// <exception cref="ArgumentNullException"><paramref name="observable"/> is null.</exception> public static Task <TResult> ToTask <TResult>(this IObservable <TResult> observable, CancellationToken cancellationToken, object state) { if (observable == null) { throw new ArgumentNullException("observable"); } var hasValue = false; var lastValue = default(TResult); var tcs = new TaskCompletionSource <TResult>(state); var disposable = new SingleAssignmentDisposable(); var ctr = default(CancellationTokenRegistration); if (cancellationToken.CanBeCanceled) { ctr = cancellationToken.Register(() => { disposable.Dispose(); tcs.TrySetCanceled(cancellationToken); }); } var taskCompletionObserver = Observer.Create <TResult>( value => { hasValue = true; lastValue = value; }, ex => { tcs.TrySetException(ex); ctr.Dispose(); // no null-check needed (struct) disposable.Dispose(); }, () => { if (hasValue) { tcs.TrySetResult(lastValue); } else { tcs.TrySetException(new InvalidOperationException("Strings_Linq.NO_ELEMENTS")); } ctr.Dispose(); // no null-check needed (struct) disposable.Dispose(); } ); // // Subtle race condition: if the source completes before we reach the line below, the SingleAssigmentDisposable // will already have been disposed. Upon assignment, the disposable resource being set will be disposed on the // spot, which may throw an exception. (Similar to TFS 487142) // try { // // [OK] Use of unsafe Subscribe: we're catching the exception here to set the TaskCompletionSource. // // Notice we could use a safe subscription to route errors through OnError, but we still need the // exception handling logic here for the reason explained above. We cannot afford to throw here // and as a result never set the TaskCompletionSource, so we tunnel everything through here. // disposable.Disposable = observable.Subscribe/*Unsafe*/ (taskCompletionObserver); } catch (Exception ex) { tcs.TrySetException(ex); } return(tcs.Task); }
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 <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 <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> 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); })); }