void RecursiveRun(Action self) { lock (gate) { this.nextSelf = self; 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; if (isFirstSubscribe) { isFirstSubscribe = false; d.Disposable = source.Subscribe(this); } else { MainThreadDispatcher.SendStartCoroutine(SubscribeAfterEndOfFrame(d, source, this, parent.lifeTimeChecker)); } } }
public IDisposable Run() { var e = default(IEnumerator <T>); try { e = parent.source.GetEnumerator(); } catch (Exception exception) { OnError(exception); return(Disposable.Empty); } if (parent.scheduler == Scheduler.Immediate) { while (true) { bool hasNext; var current = default(T); try { hasNext = e.MoveNext(); if (hasNext) { current = e.Current; } } catch (Exception ex) { e.Dispose(); try { observer.OnError(ex); } finally { Dispose(); } break; } if (hasNext) { observer.OnNext(current); } else { e.Dispose(); try { observer.OnCompleted(); } finally { Dispose(); } break; } } return(Disposable.Empty); } var flag = new SingleAssignmentDisposable(); flag.Disposable = parent.scheduler.Schedule(self => { if (flag.IsDisposed) { e.Dispose(); return; } bool hasNext; var current = default(T); try { hasNext = e.MoveNext(); if (hasNext) { current = e.Current; } } catch (Exception ex) { e.Dispose(); try { observer.OnError(ex); } finally { Dispose(); } return; } if (hasNext) { observer.OnNext(current); self(); } else { e.Dispose(); try { observer.OnCompleted(); } finally { Dispose(); } } }); return(flag); }
/// <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); }