public static IObservable <TSource> ThrottleFirstFrame <TSource>(this IObservable <TSource> source, int frameCount, FrameCountType frameCountType = FrameCountType.Update) { return(Observable.Create <TSource>(observer => { var gate = new object(); var open = true; var cancelable = new SerialDisposable(); var subscription = source.Subscribe(x => { lock (gate) { if (!open) { return; } observer.OnNext(x); open = false; } var d = new SingleAssignmentDisposable(); cancelable.Disposable = d; d.Disposable = TimerFrame(frameCount, frameCountType) .Subscribe(_ => { lock (gate) { open = true; } }); }, exception => { cancelable.Dispose(); lock (gate) { observer.OnError(exception); } }, () => { cancelable.Dispose(); lock (gate) { observer.OnCompleted(); } }); return new CompositeDisposable(subscription, cancelable); })); }
public static IObservable <TSource> ThrottleFirst <TSource>(this IObservable <TSource> source, TimeSpan dueTime, IScheduler scheduler) { return(Observable.Create <TSource>(observer => { var gate = new object(); var open = true; var cancelable = new SerialDisposable(); var subscription = source.Subscribe(x => { lock (gate) { if (!open) { return; } observer.OnNext(x); open = false; } var d = new SingleAssignmentDisposable(); cancelable.Disposable = d; d.Disposable = scheduler.Schedule(dueTime, () => { lock (gate) { open = true; } }); }, exception => { cancelable.Dispose(); lock (gate) { observer.OnError(exception); } }, () => { cancelable.Dispose(); lock (gate) { observer.OnCompleted(); } }); return new CompositeDisposable(subscription, cancelable); })); }
public static IObservable <T> SubscribeOn <T>(this IObservable <T> source, IScheduler scheduler) { return(Observable.Create <T>(observer => { var m = new SingleAssignmentDisposable(); var d = new SerialDisposable(); d.Disposable = m; m.Disposable = scheduler.Schedule(() => { d.Disposable = new ScheduledDisposable(scheduler, source.Subscribe(observer)); }); return d; })); }
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 { if (errorHandler == Stubs.CatchIgnore <T> ) { next = Observable.Empty <T>(); // for avoid iOS AOT } else { 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 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 <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 <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); })); }
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 <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 <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 void Serial() { SetScehdulerForImport(); var d = new SerialDisposable(); 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 SerialDisposable(); 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 SerialDisposable(); id1 = new IdDisp(1); id2 = new IdDisp(2); id3 = new IdDisp(3); d.Disposable = id1; id1.IsDisposed.IsFalse(); d.Disposable = id2; id1.IsDisposed.IsTrue(); id2.IsDisposed.IsFalse(); d.Disposable = id3; id2.IsDisposed.IsTrue(); id3.IsDisposed.IsFalse(); d.Dispose(); id3.IsDisposed.IsTrue(); // null d = new SerialDisposable(); 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); })); }