// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. using System.Reactive.Disposables; namespace System.Reactive.Concurrency { public static partial class Scheduler { /// /// Schedules an action to be executed recursively. /// /// Scheduler to execute the recursive action on. /// Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, Action action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule(action, (_action, self) => _action(() => self(_action))); } /// /// Schedules an action to be executed recursively. /// /// The type of the state passed to the scheduled action. /// Scheduler to execute the recursive action on. /// State passed to the action to be executed. /// Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, TState state, Action> action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule((state, action), (s, p) => InvokeRec1(s, p)); } private static IDisposable InvokeRec1(IScheduler scheduler, (TState state, Action> action) tuple) { var recursiveInvoker = new InvokeRec1State(scheduler, tuple.action); recursiveInvoker.InvokeFirst(tuple.state); return recursiveInvoker; } /// /// Schedules an action to be executed recursively after a specified relative due time. /// /// Scheduler to execute the recursive action on. /// Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action at the specified relative time. /// Relative time after which to execute the action for the first time. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, TimeSpan dueTime, Action> action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule(action, dueTime, (_action, self) => _action(dt => self(_action, dt))); } /// /// Schedules an action to be executed recursively after a specified relative due time. /// /// The type of the state passed to the scheduled action. /// Scheduler to execute the recursive action on. /// State passed to the action to be executed. /// Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in the recursive due time and invocation state. /// Relative time after which to execute the action for the first time. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, TState state, TimeSpan dueTime, Action> action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule((state, action), dueTime, (s, p) => InvokeRec2(s, p)); } private static IDisposable InvokeRec2(IScheduler scheduler, (TState state, Action> action) tuple) { var recursiveInvoker = new InvokeRec2State(scheduler, tuple.action); recursiveInvoker.InvokeFirst(tuple.state); return recursiveInvoker; } /// /// Schedules an action to be executed recursively at a specified absolute due time. /// /// Scheduler to execute the recursive action on. /// Action to execute recursively. The parameter passed to the action is used to trigger recursive scheduling of the action at the specified absolute time. /// Absolute time at which to execute the action for the first time. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, DateTimeOffset dueTime, Action> action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule(action, dueTime, (_action, self) => _action(dt => self(_action, dt))); } /// /// Schedules an action to be executed recursively at a specified absolute due time. /// /// The type of the state passed to the scheduled action. /// Scheduler to execute the recursive action on. /// State passed to the action to be executed. /// Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in the recursive due time and invocation state. /// Absolute time at which to execute the action for the first time. /// The disposable object used to cancel the scheduled action (best effort). /// or is null. public static IDisposable Schedule(this IScheduler scheduler, TState state, DateTimeOffset dueTime, Action> action) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } return scheduler.Schedule((state, action), dueTime, (s, p) => InvokeRec3(s, p)); } private static IDisposable InvokeRec3(IScheduler scheduler, (TState state, Action> action) tuple) { var recursiveInvoker = new InvokeRec3State(scheduler, tuple.action); recursiveInvoker.InvokeFirst(tuple.state); return recursiveInvoker; } private abstract class InvokeRecBaseState : IDisposable { protected readonly IScheduler Scheduler; protected readonly CompositeDisposable Group; public InvokeRecBaseState(IScheduler scheduler) { Scheduler = scheduler; Group = new CompositeDisposable(); } public void Dispose() { Group.Dispose(); } } private sealed class InvokeRec1State : InvokeRecBaseState { private readonly Action> _action; private readonly Action _recurseCallback; public InvokeRec1State(IScheduler scheduler, Action> action) : base(scheduler) { _action = action; _recurseCallback = state => InvokeNext(state); } internal void InvokeNext(TState state) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.Schedule((state, sad, @this: this), (_, nextState) => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); return Disposable.Empty; }); } internal void InvokeFirst(TState state) { _action(state, _recurseCallback); } } private sealed class InvokeRec2State : InvokeRecBaseState { private readonly Action> _action; private readonly Action _recurseCallback; public InvokeRec2State(IScheduler scheduler, Action> action) : base(scheduler) { _action = action; _recurseCallback = (state, time) => InvokeNext(state, time); } internal void InvokeNext(TState state, TimeSpan time) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.Schedule((state, sad, @this: this), time, (_, nextState) => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); return Disposable.Empty; }); } internal void InvokeFirst(TState state) { _action(state, _recurseCallback); } } private sealed class InvokeRec3State : InvokeRecBaseState { private readonly Action> _action; private readonly Action _recurseCallback; public InvokeRec3State(IScheduler scheduler, Action> action) : base(scheduler) { _action = action; _recurseCallback = (state, dtOffset) => InvokeNext(state, dtOffset); } internal void InvokeNext(TState state, DateTimeOffset dtOffset) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.Schedule((state, sad, @this: this), dtOffset, (_, nextState) => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); return Disposable.Empty; }); } internal void InvokeFirst(TState state) { _action(state, _recurseCallback); } } } }