|
|
@@ -0,0 +1,81 @@
|
|
|
+// 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.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
+
|
|
|
+namespace System.Reactive.Concurrency
|
|
|
+{
|
|
|
+ public abstract class AsyncSchedulerBase : IAsyncScheduler
|
|
|
+ {
|
|
|
+ public virtual DateTimeOffset Now => DateTimeOffset.Now;
|
|
|
+
|
|
|
+ public virtual Task<IAsyncDisposable> ScheduleAsync(Func<CancellationToken, Task> action)
|
|
|
+ {
|
|
|
+ if (action == null)
|
|
|
+ throw new ArgumentNullException(nameof(action));
|
|
|
+
|
|
|
+ return ScheduleAsyncCore(action);
|
|
|
+ }
|
|
|
+
|
|
|
+ public virtual Task<IAsyncDisposable> ScheduleAsync(Func<CancellationToken, Task> action, TimeSpan dueTime)
|
|
|
+ {
|
|
|
+ if (action == null)
|
|
|
+ throw new ArgumentNullException(nameof(action));
|
|
|
+
|
|
|
+ var dueTimeRelative = Normalize(dueTime);
|
|
|
+
|
|
|
+ return ScheduleAsyncCore(async ct =>
|
|
|
+ {
|
|
|
+ await Delay(dueTimeRelative); // NB: Honor SynchronizationContext to stay on scheduler.
|
|
|
+
|
|
|
+ await action(ct);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public virtual Task<IAsyncDisposable> ScheduleAsync(Func<CancellationToken, Task> action, DateTimeOffset dueTime)
|
|
|
+ {
|
|
|
+ if (action == null)
|
|
|
+ throw new ArgumentNullException(nameof(action));
|
|
|
+
|
|
|
+ return ScheduleAsyncCore(async ct =>
|
|
|
+ {
|
|
|
+ var dueTimeRelative = Normalize(dueTime - Now); // TODO: Support clock drift and clock changes.
|
|
|
+
|
|
|
+ await Delay(dueTimeRelative); // NB: Honor SynchronizationContext to stay on scheduler.
|
|
|
+
|
|
|
+ await action(ct);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ protected virtual async Task<IAsyncDisposable> ScheduleAsyncCore(Func<CancellationToken, Task> action)
|
|
|
+ {
|
|
|
+ var cad = new CancellationAsyncDisposable();
|
|
|
+
|
|
|
+ await ScheduleAsyncCore(action, cad.Token).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return cad;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected abstract Task ScheduleAsyncCore(Func<CancellationToken, Task> action, CancellationToken token);
|
|
|
+
|
|
|
+ protected abstract Task Delay(TimeSpan dueTime);
|
|
|
+
|
|
|
+ protected static TimeSpan Normalize(TimeSpan timeSpan) => timeSpan < TimeSpan.Zero ? TimeSpan.Zero : timeSpan;
|
|
|
+
|
|
|
+ private sealed class CancellationAsyncDisposable : IAsyncDisposable
|
|
|
+ {
|
|
|
+ private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
|
|
+
|
|
|
+ public CancellationToken Token => _cts.Token;
|
|
|
+
|
|
|
+ public Task DisposeAsync()
|
|
|
+ {
|
|
|
+ _cts.Cancel();
|
|
|
+
|
|
|
+ return Task.CompletedTask;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|