public static Task <T> WaitUntilValueChangedAsync <T>(this IReadOnlyReactiveProperty <T> source, CancellationToken cancellationToken = default(CancellationToken)) { var tcs = new CancellableTaskCompletionSource <T>(); var disposable = new SingleAssignmentDisposable(); if (source.HasValue) { // Skip first value var isFirstValue = true; disposable.Disposable = source.Subscribe(x => { if (isFirstValue) { isFirstValue = false; return; } else { disposable.Dispose(); // finish subscription. tcs.TrySetResult(x); } }, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled()); } else { disposable.Disposable = source.Subscribe(x => { disposable.Dispose(); // finish subscription. tcs.TrySetResult(x); }, ex => tcs.TrySetException(ex), () => tcs.TrySetCanceled()); } cancellationToken.Register(Callback, Tuple.Create(tcs, disposable.Disposable), false); return(tcs.Task); }
public static 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); }
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); })); }
public static IObservable <IList <T> > Zip <T> (params IObservable <T>[] sources) { return(Observable.Create <IList <T> > (observer => { var gate = new object(); var length = sources.Length; var queues = new Queue <T> [length]; for (int i = 0; i < length; i++) { queues [i] = new Queue <T> (); } var isDone = new bool[length]; Action <int> dequeue = index => { lock (gate) { if (queues.All(x => x.Count > 0)) { var result = queues.Select(x => x.Dequeue()).ToList(); observer.OnNext(result); return; } if (isDone.Where((x, i) => i != index).All(x => x)) { observer.OnCompleted(); return; } } }; var subscriptions = sources .Select((source, index) => { var d = new SingleAssignmentDisposable(); d.Disposable = source.Subscribe(x => { lock (gate) { queues [index].Enqueue(x); dequeue(index); } }, ex => { lock (gate) { observer.OnError(ex); } }, () => { lock (gate) { isDone [index] = true; if (isDone.All(x => x)) { observer.OnCompleted(); } else { d.Dispose(); } } }); return d; }) .ToArray(); return new CompositeDisposable(subscriptions) { Disposable.Create(() => { lock (gate) { foreach (var item in queues) { item.Clear(); } } }) }; })); }
public void Dispose() { _disposable.Dispose(); }
/// <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); }
void OnCanceled() { disposable.Dispose(); promise.TrySetCanceled(); }
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> 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 }; })); }
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 }; })); }