/// <summary> /// Starts the specified <paramref name="worker" />, to which it will pass the <see cref="IDeadManSwitch" /> and a cancellation token. /// </summary> /// <param name="worker">The worker that can works asynchronously and is provided with a dead man switch and a cancellation token</param> /// <param name="options">The options that specify how the dead man's switch must behave</param> /// <param name="cancellationToken">The cancellation token that is capable of immediately stopping the dead man's switch and the worker.</param> /// <typeparam name="TResult">The type of result that the worker produces</typeparam> /// <returns>The result that the worker has produced</returns> /// <exception cref="OperationCanceledException"> /// When the worked was canceled by the dead man's switch, or when the provided <paramref name="cancellationToken" /> is cancelled while /// the worker is still busy /// </exception> public static Task <TResult> RunAsync <TResult>( Func <IDeadManSwitch, CancellationToken, Task <TResult> > worker, DeadManSwitchOptions options, CancellationToken cancellationToken) { return(DeadManSwitchRunner.Value.RunAsync(new LambdaDeadManSwitchWorker <TResult>(worker), options, cancellationToken)); }
/// <inheritdoc /> public async Task RunAsync(IInfiniteDeadManSwitchWorker worker, DeadManSwitchOptions options, CancellationToken cancellationToken) { if (worker == null) { throw new ArgumentNullException(nameof(worker)); } _logger.Trace("Starting infinite worker loop for {WorkerName} using a dead man's switch", worker.Name); using (var deadManSwitchSession = _deadManSwitchSessionFactory.Create(options)) using (var watcherCTS = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { var deadManSwitch = deadManSwitchSession.DeadManSwitch; var deadManSwitchWatcher = deadManSwitchSession.DeadManSwitchWatcher; var deadManSwitchContext = deadManSwitchSession.DeadManSwitchContext; var watcherTask = Task.Factory.StartNew(() => deadManSwitchWatcher.WatchAsync(watcherCTS.Token), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); var iteration = 1; while (!cancellationToken.IsCancellationRequested) { using (var workerCTS = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, deadManSwitchContext.CancellationToken)) { _logger.Trace("Beginning work iteration {Iteration} of infinite worker {WorkerName} using a dead man's switch", iteration, worker.Name); var workerTask = Task.Run(() => worker.WorkAsync(deadManSwitch, workerCTS.Token), CancellationToken.None); try { await workerTask.ConfigureAwait(false); _logger.Debug("Worker {WorkerName} completed gracefully", worker.Name); deadManSwitch.Notify("Worker task completed gracefully"); } catch (OperationCanceledException) { _logger.Warning("Worker {WorkerName} was canceled", worker.Name); // Restart watcher await watcherTask.ConfigureAwait(false); deadManSwitch.Notify("Worker task was canceled"); watcherTask = Task.Factory.StartNew(() => deadManSwitchWatcher.WatchAsync(watcherCTS.Token), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } } iteration++; } _logger.Information("Cancellation requested, cleaning up infinite worker loop for {WorkerName}", worker.Name); watcherCTS.Cancel(); await watcherTask.ConfigureAwait(false); } _logger.Trace("Infinite worker loop for {WorkerName} has stopped", worker.Name); }
/// <summary> /// Starts the specified <paramref name="worker" />, to which it will pass a <see cref="IDeadManSwitch" /> and a cancellation token. /// </summary> /// <param name="worker">The worker that can perform work asynchronously</param> /// <param name="options">The options that specify how the dead man's switch must behave</param> /// <param name="cancellationToken">The cancellation token that is capable of immediately stopping the dead man's switch and the worker.</param> /// <returns>A task that will complete when the provided <paramref name="cancellationToken" /> is cancelled.</returns> /// <exception cref="Exception">When the worker throws an exception, this will not be caught</exception> public static Task RunAsync(Func <IDeadManSwitch, CancellationToken, Task> worker, DeadManSwitchOptions options, CancellationToken cancellationToken) { return(InfiniteDeadManSwitchRunner.Value.RunAsync(new LambdaInfiniteDeadManSwitchWorker(worker), options, cancellationToken)); }
/// <inheritdoc /> public async Task <TResult> RunAsync <TResult>(IDeadManSwitchWorker <TResult> worker, DeadManSwitchOptions options, CancellationToken cancellationToken) { if (worker == null) { throw new ArgumentNullException(nameof(worker)); } using (var deadManSwitchSession = _deadManSwitchSessionFactory.Create(options)) using (var watcherCTS = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) using (var workerCTS = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, deadManSwitchSession.DeadManSwitchContext.CancellationTokenSource.Token)) { _logger.Trace("Running worker {WorkerName} using a dead man's switch", worker.Name); var deadManSwitch = deadManSwitchSession.DeadManSwitch; var deadManSwitchWatcher = deadManSwitchSession.DeadManSwitchWatcher; var workerTask = Task.Run(async() => await worker.WorkAsync(deadManSwitch, workerCTS.Token).ConfigureAwait(false), CancellationToken.None); var watcherTask = Task.Run(async() => await deadManSwitchWatcher.WatchAsync(watcherCTS.Token).ConfigureAwait(false), CancellationToken.None); var task = await Task.WhenAny(workerTask, watcherTask).ConfigureAwait(false); if (task == workerTask) { watcherCTS.Cancel(); return(await workerTask.ConfigureAwait(false)); } workerCTS.Cancel(); throw new OperationCanceledException(workerCTS.Token); } }