private static IDisposable InvokeTaskWithDisposable <TState>( IScheduler scheduler, TState state, Func <CancellationToken, IScheduler, TState, Task <IDisposable> > taskBuilder, SchedulerSynchronizationContext schedulerContext) { var subscriptions = new SerialDisposable(); var cancellationDisposable = new CancellationDisposable().DisposeWith(subscriptions); using (SynchronizationContextHelper.ScopedSet(schedulerContext)) { taskBuilder(cancellationDisposable.Token, scheduler, state) .ContinueWith(t => { if (t.IsCanceled) { subscriptions.Disposable = null; } else if (t.IsFaulted) { subscriptions.Disposable = scheduler.Schedule(() => { t.Exception.Handle(e => e is OperationCanceledException); }); } else { subscriptions.Disposable = t.Result; } }, TaskContinuationOptions.ExecuteSynchronously); } return(subscriptions); }
/// <summary> /// Schedules work using an asynchronous method, allowing for cooperative scheduling in an imperative coding style. /// </summary> /// <param name="scheduler">Scheduler to schedule work on.</param> /// <param name="state">State to pass to the asynchronous method.</param> /// <param name="taskBuilder">Asynchronous method to run the work.</param> /// <returns>Disposable object that allows to cancel outstanding work on cooperative cancellation points or through the cancellation token passed to the asynchronous method.</returns> public static IDisposable ScheduleTask <TState>( this IScheduler scheduler, TState state, Func <CancellationToken, IScheduler, TState, Task> taskBuilder) { var schedulerContext = new SchedulerSynchronizationContext(scheduler); return(scheduler.Schedule(state, (s, st) => InvokeTask(scheduler, st, taskBuilder, schedulerContext))); }
/// <summary> /// Schedulers the specified task on the specified scheduler. /// </summary> /// <param name="source"></param> /// <param name="taskBuilder"></param> /// <returns></returns> public static IDisposable Schedule(this IScheduler source, Func <CancellationToken, Task> taskBuilder) { var subscriptions = new CompositeDisposable(2); var cancellationDisposable = new CancellationDisposable(); // Capture the source context before calling schedule. var sourceCtx = new SchedulerSynchronizationContext(source); // It is acceptable to use the async void pattern as long as the // synchronization context is not the default, i.e. executing on the thread pool, // where exceptions are not trapped properly. System.Reactive.Concurrency.Scheduler.Schedule( source, () => { using (SynchronizationContextHelper.ScopedSet(sourceCtx)) { taskBuilder(cancellationDisposable.Token) .ContinueWith(t => { if (t.IsFaulted) { source.Schedule(() => { throw t.Exception; }); } } ); } } ).DisposeWith(subscriptions); cancellationDisposable.DisposeWith(subscriptions); return(subscriptions); }