// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT 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, static (a, self) => a(() => self(a))); } /// /// 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), static (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, static (a, self) => a(ts => self(a, ts))); } /// /// 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, static (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, static (a, self) => a(dt => self(a, 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, static (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; protected 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); } private void InvokeNext(TState state) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.ScheduleAction((state, sad, @this: this), static nextState => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); }); } 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); } private void InvokeNext(TState state, TimeSpan time) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.ScheduleAction((state, sad, @this: this), time, static nextState => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); }); } 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); } private void InvokeNext(TState state, DateTimeOffset dtOffset) { var sad = new SingleAssignmentDisposable(); Group.Add(sad); sad.Disposable = Scheduler.ScheduleAction((state, sad, @this: this), dtOffset, static nextState => { nextState.@this.Group.Remove(nextState.sad); nextState.@this.InvokeFirst(nextState.state); }); } internal void InvokeFirst(TState state) { _action(state, _recurseCallback); } } } }