// 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. #if !WINDOWS using System.Reactive.Disposables; using System.Threading; namespace System.Reactive.Concurrency { /// /// Represents an object that schedules units of work on a . /// /// /// This scheduler type is typically used indirectly through the and methods that use the Dispatcher on the calling thread. /// public class DispatcherScheduler : LocalScheduler, ISchedulerPeriodic { /// /// Gets the scheduler that schedules work on the current . /// [Obsolete(Constants_WindowsThreading.OBSOLETE_INSTANCE_PROPERTY)] public static DispatcherScheduler Instance => new DispatcherScheduler(System.Windows.Threading.Dispatcher.CurrentDispatcher); /// /// Gets the scheduler that schedules work on the for the current thread. /// public static DispatcherScheduler Current { get { var dispatcher = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread); if (dispatcher == null) throw new InvalidOperationException(Strings_WindowsThreading.NO_DISPATCHER_CURRENT_THREAD); return new DispatcherScheduler(dispatcher); } } /// /// Constructs a that schedules units of work on the given . /// /// to schedule work on. /// is null. public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher) { if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher)); Dispatcher = dispatcher; Priority = Windows.Threading.DispatcherPriority.Normal; } /// /// Constructs a that schedules units of work on the given at the given priority. /// /// to schedule work on. /// Priority at which units of work are scheduled. /// is null. public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher, System.Windows.Threading.DispatcherPriority priority) { if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher)); Dispatcher = dispatcher; Priority = priority; } /// /// Gets the associated with the . /// public System.Windows.Threading.Dispatcher Dispatcher { get; } /// /// Gets the priority at which work items will be dispatched. /// public System.Windows.Threading.DispatcherPriority Priority { get; } /// /// Schedules an action to be executed on the dispatcher. /// /// The type of the state passed to the scheduled action. /// State passed to the action to be executed. /// Action to be executed. /// The disposable object used to cancel the scheduled action (best effort). /// is null. public override IDisposable Schedule(TState state, Func action) { if (action == null) throw new ArgumentNullException(nameof(action)); var d = new SingleAssignmentDisposable(); Dispatcher.BeginInvoke( new Action(() => { if (!d.IsDisposed) { d.Disposable = action(this, state); } }), Priority ); return d; } /// /// Schedules an action to be executed after on the dispatcher, using a object. /// /// The type of the state passed to the scheduled action. /// State passed to the action to be executed. /// Action to be executed. /// Relative time after which to execute the action. /// The disposable object used to cancel the scheduled action (best effort). /// is null. public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) { if (action == null) throw new ArgumentNullException(nameof(action)); var dt = Scheduler.Normalize(dueTime); if (dt.Ticks == 0) { return Schedule(state, action); } return ScheduleSlow(state, dt, action); } private IDisposable ScheduleSlow(TState state, TimeSpan dueTime, Func action) { var d = new MultipleAssignmentDisposable(); var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher); timer.Tick += (s, e) => { var t = Interlocked.Exchange(ref timer, null); if (t != null) { try { d.Disposable = action(this, state); } finally { t.Stop(); action = null; } } }; timer.Interval = dueTime; timer.Start(); d.Disposable = Disposable.Create(() => { var t = Interlocked.Exchange(ref timer, null); if (t != null) { t.Stop(); action = (_, __) => Disposable.Empty; } }); return d; } /// /// Schedules a periodic piece of work on the dispatcher, using a object. /// /// The type of the state passed to the scheduled action. /// Initial state passed to the action upon the first iteration. /// Period for running the work periodically. /// Action to be executed, potentially updating the state. /// The disposable object used to cancel the scheduled recurring action (best effort). /// is null. /// is less than . public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action) { if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period)); if (action == null) throw new ArgumentNullException(nameof(action)); var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher); var state1 = state; timer.Tick += (s, e) => { state1 = action(state1); }; timer.Interval = period; timer.Start(); return Disposable.Create(() => { var t = Interlocked.Exchange(ref timer, null); if (t != null) { t.Stop(); action = _ => _; } }); } } } #endif