public static IDisposable SubscribeWithContext <T>(
            this IObservable <T> source,
            Func <T, Task> action,
            ConcurrentExecutionMode mode = ConcurrentExecutionMode.AbortPrevious,
            TimeSpan?retryDelay          = null)
        {
            var ctx = AsyncContext.Current;

            if (ctx == null)
            {
                throw new InvalidOperationException("This method can only be used in the scope of an AsyncContext.");
            }

            return(source
                   .Execute(Execute, mode, ctx.Scheduler)
                   .Retry(retryDelay ?? Constants.DefaultRetryDelay, ctx.Scheduler)
                   .Subscribe());

            Task Execute(CancellationToken ct, T value)
            => AsyncContext.Execute(ct, value, action);
        }
        protected IDisposable At(TimeSpan timeOfDay, Func <DateTimeOffset, Task> operation, ConcurrentExecutionMode mode = ConcurrentExecutionMode.AbortPrevious)
        {
            var now = Scheduler.Now.LocalDateTime;
            var day = now.Date;

            if (now.TimeOfDay > timeOfDay)
            {
                day += TimeSpan.FromDays(1);
            }

            return(Observable
                   .Timer(day + timeOfDay, TimeSpan.FromHours(24), Scheduler)
                   .SubscribeWithContext(_ => operation(Scheduler.Now.ToLocalTime()), mode));
        }
        protected IDisposable At(IObservable <TimeSpan> timeOfDay, Func <DateTimeOffset, Task> operation, ConcurrentExecutionMode mode = ConcurrentExecutionMode.AbortPrevious)
        {
            return(timeOfDay
                   .DistinctUntilChanged()
                   .Select(tod =>
            {
                var now = Scheduler.Now.LocalDateTime;
                var day = now.Date;
                if (now.TimeOfDay > tod)
                {
                    day += TimeSpan.FromDays(1);
                }

                return Observable.Timer(day + tod, TimeSpan.FromHours(24), Scheduler);
            })
                   .Switch()
                   .SubscribeWithContext(_ => operation(Scheduler.Now.ToLocalTime()), mode));
        }
        /// <summary>
        /// Executes an asynchronous action for each (depending of the <paramref name="mode"/>) value produced by an observable sequence
        /// </summary>
        /// <typeparam name="T">Type of the element in the source observable sequence</typeparam>
        /// <param name="source">The source observable sequence</param>
        /// <param name="action">Action to execute</param>
        /// <param name="mode">
        /// Configures how to behave in case of a new element is produced by the <paramref name="source"/>
        /// while a previous execution of the <paramref name="action"/> is still pending.
        /// </param>
        /// <param name="scheduler">The scheduler to use to run <paramref name="action"/>.</param>
        /// <returns>An observable sequence of <see cref="Unit"/> which produce a value each time an execution of the action completes.</returns>
        public static IObservable <Unit> Execute <T>(this IObservable <T> source, Func <CancellationToken, T, Task> action, ConcurrentExecutionMode mode, IScheduler scheduler)
        {
            var originalAction = action;

            action = async(ct, t) =>
            {
                try
                {
                    await originalAction(ct, t);
                }
                catch (OperationCanceledException)
                {
                }
            };

            switch (mode)
            {
            case ConcurrentExecutionMode.AbortPrevious:
                return(source
                       .Select(d => Observable.FromAsync(ct => action(ct, d), scheduler))
                       .Switch());

            case ConcurrentExecutionMode.Ignore:
                var running = 0;
                return(source
                       .SelectMany(d => Observable.FromAsync(
                                       async ct =>
                {
                    if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
                    {
                        try
                        {
                            await action(ct, d);
                        }
                        finally
                        {
                            running = 0;
                        }
                    }
                },
                                       scheduler)));

            case ConcurrentExecutionMode.RunConcurrently:
                return(source.SelectMany(d => Observable.FromAsync(ct => action(ct, d), scheduler)));

            case ConcurrentExecutionMode.Queue:
                var @lock = new Utils.AsyncLock();
                return(source
                       .SelectMany(d => Observable.FromAsync(
                                       async ct =>
                {
                    using (await @lock.LockAsync(ct))
                    {
                        await action(ct, d);
                    }
                },
                                       scheduler)));

            default:
                throw new ArgumentOutOfRangeException(nameof(mode));
            }
        }