public async Task ShouldCancelTheTaskIfTheDeadManSwitchWasPausedAndResumedAndTheTaskTakesTooLong() { using (var cts = new CancellationTokenSource()) { // Arrange double?pi = null, e = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var workItems = WorkItems( Work( Pause(), Sleep(TimeSpan.FromSeconds(3)), Do(_ => pi = Math.PI), Resume(), Sleep(TimeSpan.FromSeconds(3)), Do(_ => e = Math.E) ), Work( Do(_ => _logger.LogInformation("Cancelling infinite worker")), Do(_ => cts.Cancel()) ) ); var worker = InfiniteWorker(workItems); // Act await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert pi.Should().NotBeNull(); e.Should().BeNull(); } }
public async Task ShouldNotCancelTheTaskIfTheDeadManSwitchIsPausedMultipleTimesAndTheTaskTakesTooLong() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Pause(), Sleep(TimeSpan.FromSeconds(3)), Pause(), Pause(), Resume() ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); } }
public async Task ShouldCancelTheTaskIfItTakesTooLongAfterResuming() { using (var cts = new CancellationTokenSource()) { // Arrange double?e = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Pause(), Notify("Sleeping 3s"), Sleep(TimeSpan.FromSeconds(3)), Notify("Calculating PI"), Resume(), Notify("Sleeping 3s"), Sleep(TimeSpan.FromSeconds(3)), Notify("Calculating E"), Do(_ => e = Math.E) ), Result(Math.PI) ); // Act await _runner.Invoking(m => m.RunAsync(worker, options, cts.Token)).Should().ThrowAsync <OperationCanceledException>().ConfigureAwait(false); // Assert e.Should().BeNull(); } }
public async Task ShouldCancelTheTaskWhenTasksTimeOutAndWhenCancelling() { using (var cts = new CancellationTokenSource()) { // Arrange double?pi = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(3) }; var workItems = WorkItems( Work( Sleep(TimeSpan.FromSeconds(4)), Do(_ => pi = Math.PI) ), Work( Do(_ => _logger.LogInformation("Cancelling infinite worker")), Do(_ => cts.Cancel()) ) ); var worker = InfiniteWorker(workItems); // Act var run = _runner.RunAsync(worker, options, cts.Token); await Task.Delay(TimeSpan.FromMilliseconds(10)).ConfigureAwait(false); cts.Cancel(); await run.ConfigureAwait(false); // Assert pi.Should().BeNull(); } }
public async Task ShouldNotCancelTheTaskIfItTakesTooLongAfterPausingAndResumingAndPausingAgain() { using (var cts = new CancellationTokenSource()) { // Arrange double?e = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(4) }; var worker = Worker( Work( Pause(), Notify("Sleeping 6s"), Sleep(TimeSpan.FromSeconds(6)), Notify("Calculating PI"), Resume(), Pause(), Notify("Sleeping 6s"), Sleep(TimeSpan.FromSeconds(6)), Notify("Calculating E"), Do(_ => e = Math.E) ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); e.Should().Be(Math.E); } }
public async Task ShouldRunTaskMultipleTimesUntilStopped() { using (var cts = new CancellationTokenSource()) { // Arrange var pies = new List <double>(); var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = InfiniteWorker( WorkItems( Work( Do(_ => pies.Add(Math.PI)) ), Work( Do(_ => pies.Add(Math.PI))), Work( Do(_ => pies.Add(Math.PI)) ), Work( Do(_ => _logger.LogInformation("Cancelling infinite worker")), Do(_ => cts.Cancel()) ) )); // Act await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert pies.Should().HaveCount(3); pies.Should().AllBeEquivalentTo(Math.PI); } }
public async Task ShouldHandleNotificationsInParallel(int numberOfNotifications) { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(5), NumberOfNotificationsToKeep = 3 }; var worker = Worker( Work( async(deadManSwitch, cancellationToken) => { var sendNotifications = Enumerable.Range(0, numberOfNotifications) .AsParallel() .WithDegreeOfParallelism(100) .Select(i => Task.Run(() => deadManSwitch.Notify("Notification " + i))); await Task.WhenAll(sendNotifications).ConfigureAwait(false); } ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); _sessionFactory.Session.Should().NotBeNull(); var notifications = _sessionFactory.Session.DeadManSwitchContext.Notifications; notifications.Should().HaveCount(3); } }
public async Task ShouldContainNotificationsRespectingNumberOfNotificationsToKeep() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(5), NumberOfNotificationsToKeep = 3 }; var worker = Worker( Work( Notify("Notification 1"), Notify("Notification 2"), Notify("Notification 3"), Notify("Notification 4") ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); _sessionFactory.Session.Should().NotBeNull(); var notifications = _sessionFactory.Session.DeadManSwitchContext.Notifications; string[] expected = { "Notification 2", "Notification 3", "Notification 4" }; var actual = notifications.Select(n => n.Content).ToArray(); actual.Should().BeEquivalentTo(expected); } }
/// <summary> /// Demonstrates how to run (and stop) an infinite worker, using a dead man's switch /// </summary> public static async Task Main() { var serviceProvider = new ServiceCollection() .AddLogging(builder => builder.AddConsole()) .AddDeadManSwitch() .BuildServiceProvider(); var infiniteRunner = serviceProvider.GetRequiredService <IInfiniteDeadManSwitchRunner>(); var worker = new ExampleInfiniteWorker(); using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; // do not await this, it will never complete until you cancel the token var run = infiniteRunner.RunAsync(worker, options, cancellationTokenSource.Token); // let it run for 10s. await Task.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token).ConfigureAwait(false); // now stop the infinite worker cancellationTokenSource.Cancel(); // let it finish gracefully await run.ConfigureAwait(false); } }
/// <summary> /// Demonstrates how to run (and stop) an infinite worker, using a dead man's switch /// </summary> public static async Task Main() { // This example uses NLog, but it only requires a trivial amount of code to use any other logging library var loggerFactory = new NLoggerFactory(); var infiniteRunner = InfiniteDeadManSwitchRunner.Create(loggerFactory); var worker = new ExampleInfiniteWorker(); using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; // do not await this, it will never complete until you cancel the token var run = infiniteRunner.RunAsync(worker, options, cancellationTokenSource.Token); // let it run for 10s. await Task.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token).ConfigureAwait(false); // now stop the infinite worker cancellationTokenSource.Cancel(); // let it finish gracefully await run.ConfigureAwait(false); } }
public async Task ShouldCancelTheTaskWhenTheTokenIsCancelled() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Sleep(TimeSpan.FromSeconds(3)) ), Result(Math.PI) ); // Act var runTask = _runner.RunAsync(worker, options, cts.Token); await Task.Delay(TimeSpan.FromSeconds(0.5)).ConfigureAwait(false); cts.Cancel(); await runTask.Invoking(async task => await task.ConfigureAwait(false)).Should().ThrowAsync <OperationCanceledException>().ConfigureAwait(false); } }
public async Task ShouldCancelTheTaskIfItTakesTooLongToDoSomethingAndThenBeAbleToRunAgainAndCompleteImmediately() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Notify("Computing PI"), Sleep(TimeSpan.FromSeconds(3))), Result(Math.PI) ); // Act + Assert await _runner.Invoking(m => m.RunAsync(worker, options, cts.Token)).Should().ThrowAsync <OperationCanceledException>().ConfigureAwait(false); // Arrange worker = Worker( Work( Notify("Computing PI"), Sleep(TimeSpan.FromSeconds(1)) ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); } }
public DeadManSwitchContext(DeadManSwitchOptions deadManSwitchOptions) { if (deadManSwitchOptions == null) { throw new ArgumentNullException(nameof(deadManSwitchOptions)); } var notifications = Channel.CreateBounded <DeadManSwitchNotification>(new BoundedChannelOptions(deadManSwitchOptions.NumberOfNotificationsToKeep) { SingleWriter = false, SingleReader = true, FullMode = BoundedChannelFullMode.DropOldest }); var statuses = Channel.CreateUnbounded <DeadManSwitchStatus>(new UnboundedChannelOptions { SingleWriter = false, SingleReader = true }); _numberOfNotificationsToKeep = deadManSwitchOptions.NumberOfNotificationsToKeep; _notificationsReader = notifications.Reader; _notificationsWriter = notifications.Writer; _statusesReader = statuses.Reader; _statusesWriter = statuses.Writer; CancellationTokenSource = new CancellationTokenSource(); }
/// <summary> /// Demonstrates how you can run a worker once, using a dead man's switch /// </summary> public static async Task Main() { // This example uses NLog, but it only requires a trivial amount of code to use any other logging library. var loggerFactory = new NLoggerFactory(); // You can also use Create() which disables logging var runner = DeadManSwitchRunner.Create(loggerFactory); var worker = new ExampleWorker(); using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; var run = runner.RunAsync(worker, options, cancellationTokenSource.Token); // if you want to cancel at some point: cancellationTokenSource.Cancel(); var result = await run.ConfigureAwait(false); Debug.Assert(result == Math.PI); } }
/// <summary> /// Demonstrates how you can run a worker once, using a dead man's switch /// </summary> public static async Task Main() { var serviceProvider = new ServiceCollection() .AddLogging(builder => builder.AddConsole()) .AddDeadManSwitch() .BuildServiceProvider(); var runner = serviceProvider.GetRequiredService <IDeadManSwitchRunner>(); var worker = new ExampleWorker(); using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; var run = runner.RunAsync(worker, options, cancellationTokenSource.Token); // if you want to cancel at some point: cancellationTokenSource.Cancel(); var result = await run.ConfigureAwait(false); Debug.Assert(result.Equals(Math.PI)); } }
public async Task ShouldLetTaskFinishIfItNotifiesTheDeadManSwitchWithinTheTimeout() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Notify("Sleeping for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Notify("Sleeping for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Notify("Sleeping for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Notify("Computing PI") ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); } }
/// <summary> /// Demonstrates how to run (and stop) an infinite worker, using a dead man's switch /// </summary> public static async Task Main() { using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; // do not await this, it will never complete until you cancel the token var run = InfiniteDeadManSwitchTask.RunAsync(async(deadManSwitch, cancellationToken) => { deadManSwitch.Notify("Beginning work again"); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); deadManSwitch.Notify("Still busy, please don't cancel"); await DoSomethingUseful(cancellationToken).ConfigureAwait(false); }, options, cancellationTokenSource.Token); // let it run for 10s. await Task.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token).ConfigureAwait(false); // now stop the infinite worker cancellationTokenSource.Cancel(); // let it finish gracefully await run.ConfigureAwait(false); } }
/// <summary> /// Demonstrates how you can run a worker once, using a dead man's switch /// </summary> public static async Task Main() { using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; var run = DeadManSwitchTask.RunAsync(async(deadManSwitch, cancellationToken) => { deadManSwitch.Notify("Beginning work"); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); deadManSwitch.Notify("Still busy, please don't cancel"); // tell the dead man's switch to stop the clock deadManSwitch.Suspend(); // tell the dead man's switch to resume the clock deadManSwitch.Resume(); return(Math.PI); }, options, cancellationTokenSource.Token); // if you want to cancel at some point: cancellationTokenSource.Cancel(); var result = await run.ConfigureAwait(false); Debug.Assert(result.Equals(Math.PI)); } }
public DeadManSwitchContext(DeadManSwitchOptions deadManSwitchOptions) { _deadManSwitchOptions = deadManSwitchOptions ?? throw new ArgumentNullException(nameof(deadManSwitchOptions)); _notifications = new DeadManSwitchNotification[_deadManSwitchOptions.NumberOfNotificationsToKeep]; _lastNotifiedTicks = DateTime.UtcNow.Ticks; _notificationsNextItemIndex = 0; _notificationsSyncRoot = new object(); _cancellationTokenSource = new CancellationTokenSource(); }
public DeadManSwitchWatcher(IDeadManSwitchContext deadManSwitchContext, DeadManSwitchOptions deadManSwitchOptions, IDeadManSwitchTriggerer deadManSwitchTriggerer, IDeadManSwitchLogger <DeadManSwitchWatcher> logger) { _context = deadManSwitchContext ?? throw new ArgumentNullException(nameof(deadManSwitchContext)); _options = deadManSwitchOptions ?? throw new ArgumentNullException(nameof(deadManSwitchOptions)); _deadManSwitchTriggerer = deadManSwitchTriggerer ?? throw new ArgumentNullException(nameof(deadManSwitchTriggerer)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); }
public DeadManSwitchBenchmarks() { _deadManSwitchRunner = DeadManSwitchRunner.Create(); _infiniteDeadManSwitchRunner = InfiniteDeadManSwitchRunner.Create(); _deadManSwitchOptions = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(1), NumberOfNotificationsToKeep = 5 }; }
public IDeadManSwitchSession Create(DeadManSwitchOptions deadManSwitchOptions) { var deadManSwitchContext = new DeadManSwitchContext(deadManSwitchOptions); var deadManSwitch = new DeadManSwitch(deadManSwitchContext, _loggerFactory.CreateLogger <DeadManSwitch>()); var deadManSwitchTriggerer = new DeadManSwitchTriggerer(deadManSwitchContext, deadManSwitchOptions, _loggerFactory.CreateLogger <DeadManSwitchTriggerer>()); var deadManSwitchWatcher = new DeadManSwitchWatcher(deadManSwitchContext, deadManSwitchOptions, deadManSwitchTriggerer, _loggerFactory.CreateLogger <DeadManSwitchWatcher>()); return(new DeadManSwitchSession(deadManSwitchContext, deadManSwitch, deadManSwitchWatcher)); }
public async Task ShouldLetTaskFinishIfItCompletesImmediately() { using (var cts = new CancellationTokenSource()) { // Arrange var timeout = TimeSpan.FromSeconds(2); var worker = Worker(Work(), Result(Math.PI)); var options = new DeadManSwitchOptions { Timeout = timeout }; // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); } }
public async Task ShouldCancelTheTaskIfItTakesTooLongWithoutEverNotifyingTheDeadManSwitch() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Sleep(TimeSpan.FromSeconds(3)) ), Result(Math.PI) ); // Act + Assert await _runner.Invoking(m => m.RunAsync(worker, options, cts.Token)).Should().ThrowAsync <OperationCanceledException>().ConfigureAwait(false); } }
/// <summary> /// Demonstrates how you can run a worker once, using a dead man's switch /// </summary> public static async Task Main() { var loggerFactory = new NLoggerFactory(); var runner = DeadManSwitchRunner.Create(loggerFactory); var worker = new ExampleWorker(); using (var cancellationTokenSource = new CancellationTokenSource()) { var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(60) }; var run = runner.RunAsync(worker, options, cancellationTokenSource.Token); // if you want to cancel at some point: cancellationTokenSource.Cancel(); var result = await run.ConfigureAwait(false); Debug.Assert(result == Math.PI); } }
public async Task ShouldLetTaskFinishIfItRunsQuicklyEnoughWithoutDeadManSwitchNotifications() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var worker = Worker( Work( Sleep(TimeSpan.FromSeconds(1)) ), Result(Math.PI) ); // Act var result = await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert result.Should().Be(Math.PI); } }
public async Task ShouldNotStopTaskIfTaskIsNotifyingTheDeadManSwitch() { using (var cts = new CancellationTokenSource()) { // Arrange double?pi = null; double?e = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var workItems = WorkItems( Work( Notify("Going to sleep for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Notify("Going to sleep for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Notify("Going to sleep for 1 second"), Sleep(TimeSpan.FromSeconds(1)), Do(_ => e = Math.E) ), Work( Do(_ => pi = Math.PI) ), Work( Do(_ => _logger.LogInformation("Cancelling infinite worker")), Do(_ => cts.Cancel()) ) ); var worker = InfiniteWorker(workItems); // Act await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert e.Should().Be(Math.E); pi.Should().Be(Math.PI); } }
public async Task ShouldCancelTheTaskIfItTakesTooLongSynchronously() { using (var cts = new CancellationTokenSource()) { // Arrange var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(5) }; var worker = Worker( Work( (deadManSwitch, cancellationToken) => { Thread.Sleep(6000); return(Task.CompletedTask); } ), Result(Math.PI) ); // Act await _runner.Invoking(m => m.RunAsync(worker, options, cts.Token)).Should().ThrowAsync <OperationCanceledException>().ConfigureAwait(false); } }
public async Task ShouldLetTaskFinishIfItCompletesImmediately() { using (var cts = new CancellationTokenSource()) { // Arrange double?pi = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var workItems = WorkItems( Work(Do(_ => pi = Math.PI)), Do(_ => _logger.LogInformation("Cancelling infinite worker")), Work(Do(_ => cts.Cancel())) ); var worker = InfiniteWorker(workItems); // Act await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert pi.Should().Be(Math.PI); } }
public async Task ShouldRunTaskAgainAfterStoppingItTheFirstTime() { using (var cts = new CancellationTokenSource()) { // Arrange double?pi = null; double?e = null; var options = new DeadManSwitchOptions { Timeout = TimeSpan.FromSeconds(2) }; var workItems = WorkItems( Work( Notify("Going to sleep for 5 seconds"), Do(_ => _logger.LogInformation("In loop 1, this should be cancelled")), Sleep(TimeSpan.FromSeconds(5)), Do(_ => _logger.LogInformation("In loop 1, task was not aborted!!!")), Do(_ => e = Math.E) ), Work( Do(_ => _logger.LogInformation("In loop 2, assigning Math.PI")), Do(_ => pi = Math.PI) ), Work( Do(_ => _logger.LogInformation("In loop 3, stopping worker")), Do(_ => cts.Cancel()) ) ); var worker = InfiniteWorker(workItems); // Act await _runner.RunAsync(worker, options, cts.Token).ConfigureAwait(false); // Assert e.Should().BeNull(); pi.Should().Be(Math.PI); } }