Пример #1
0
                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);
            }
Пример #4
0
        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);
        }
Пример #5
0
        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);
            }));
        }
Пример #6
0
        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();
                            }
                        }
                    })
                };
            }));
        }
Пример #7
0
        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;
            }));
        }
Пример #10
0
        /// <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);
            }));
        }
Пример #11
0
 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
                };
            }));
        }
Пример #16
0
        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();
                    }
                }));
            }));
        }
Пример #17
0
        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);
        }
Пример #19
0
        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);
            }));
        }