コード例 #1
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);
        }
コード例 #2
0
        public static Task <T> WaitUntilExecuteAsync <T>(this IReactiveCommand <T> source, CancellationToken cancellationToken = default(CancellationToken))
        {
            var tcs = new CancellableTaskCompletionSource <T>();

            var disposable = new SingleAssignmentDisposable();

            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);
        }
コード例 #3
0
        public static IObservable <T> SkipUntil <T, TOther>(this IObservable <T> source, IObservable <TOther> other)
        {
            return(Observable.Create <T>(observer =>
            {
                var sourceSubscription = new SingleAssignmentDisposable();
                var otherSubscription = new SingleAssignmentDisposable();

                var open = false;

                var gate = new object();

                sourceSubscription.Disposable = source.Synchronize(gate).Subscribe(
                    x =>
                {
                    if (open)
                    {
                        observer.OnNext(x);
                    }
                },
                    observer.OnError,
                    () =>
                {
                    if (open)
                    {
                        observer.OnCompleted();
                    }
                }
                    );

                otherSubscription.Disposable = other.Synchronize(gate).Subscribe(
                    x =>
                {
                    open = true;
                    otherSubscription.Dispose();
                },
                    observer.OnError
                    );

                return new CompositeDisposable(sourceSubscription, otherSubscription);
            }));
        }
コード例 #4
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();
                            }
                        }
                    })
                };
            }));
        }
コード例 #5
0
 public void Dispose()
 {
     _disposable.Dispose();
 }
コード例 #6
0
        /// <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);
        }
コード例 #7
0
 void OnCanceled()
 {
     disposable.Dispose();
     promise.TrySetCanceled();
 }
コード例 #8
0
ファイル: TestImporter.cs プロジェクト: neuecc/UniRx
 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();
 }
コード例 #9
0
        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();
                    }
                }));
            }));
        }
コード例 #10
0
        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
                };
            }));
        }
コード例 #11
0
        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
                };
            }));
        }