public void StopAsync_DoesNotWaitForSubsequentWaitToCompleteTask() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executeStarted.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(2000))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard // Wait for the background thread to enter the long wait. Thread.Sleep(5); CancellationToken cancellationToken = CancellationToken.None; // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); Assert.True(task.WaitUntilCompleted(1000)); Assert.True(task.IsCompleted); } } }
public void StopAsync_WhenCanceled_DoesNotWaitForExecuteToFinishToCompleteTask() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executeStarted.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(2000))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard CancellationToken cancellationToken = new CancellationToken(canceled: true); // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); Assert.True(task.WaitUntilCompleted(1000)); Assert.True(task.IsCompleted); } } }
private async Task ExecuteWithOutputLogsAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TextWriter consoleOutput, IFunctionOutputDefinition outputDefinition, IDictionary <string, ParameterLog> parameterLogCollector, CancellationToken cancellationToken) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyDictionary <string, IWatcher> watches = CreateWatches(parameters); IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(watches, consoleOutput); using (ITaskSeriesTimer updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _backgroundExceptionDispatcher)) { try { await ExecuteWithWatchersAsync(invoker, parameters, cancellationToken); if (updateParameterLogTimer != null) { // Stop the watches after calling IValueBinder.SetValue (it may do things that should show up in // the watches). // Also, IValueBinder.SetValue could also take a long time (flushing large caches), and so it's // useful to have watches still running. await updateParameterLogTimer.StopAsync(cancellationToken); } } finally { ValueWatcher.AddLogs(watches, parameterLogCollector); } } }
internal async Task ProcessMessageAsync(IStorageQueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { if (!await _queueProcessor.BeginProcessingMessageAsync(message.SdkObject, cancellationToken)) { return; } FunctionResult result = null; using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _backgroundExceptionDispatcher)) { timer.Start(); result = await _triggerExecutor.ExecuteAsync(message, cancellationToken); await timer.StopAsync(cancellationToken); } await _queueProcessor.CompleteProcessingMessageAsync(message.SdkObject, result, cancellationToken); } catch (OperationCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (Exception exception) { // Immediately report any unhandled exception from this background task. // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until // Stop is called, which might never happen.) _backgroundExceptionDispatcher.Throw(ExceptionDispatchInfo.Capture(exception)); } }
public void Cancel_TriggersCommandCancellationToken() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle executeFinished = new ManualResetEvent(initialState: false)) { bool cancellationTokenSignalled = false; ITaskSeriesCommand command = CreateCommand((cancellationToken) => { Assert.True(executeStarted.Set()); // Guard cancellationTokenSignalled = cancellationToken.WaitHandle.WaitOne(1000); Assert.True(executeFinished.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(0))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard // Act product.Cancel(); // Assert Assert.True(executeFinished.WaitOne(1000)); // Guard Assert.True(cancellationTokenSignalled); // Cleanup product.StopAsync(CancellationToken.None).GetAwaiter().GetResult(); } } }
public async Task StopAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); _timer.Cancel(); await Task.WhenAll(_processing); await _timer.StopAsync(cancellationToken); }
public void StopAsync_IfAlreadyStopped_Throws() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.Zero); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); product.StopAsync(CancellationToken.None).GetAwaiter().GetResult(); CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsInvalidOperation(() => product.StopAsync(cancellationToken), "The timer has already been stopped."); } }
private async Task ProcessMessageAsync(IStorageQueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { bool succeeded; using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _backgroundExceptionDispatcher)) { timer.Start(); succeeded = await _triggerExecutor.ExecuteAsync(message, cancellationToken); await timer.StopAsync(cancellationToken); } // Need to call Delete message only if function succeeded. if (succeeded) { await DeleteMessageAsync(message, cancellationToken); } else if (_poisonQueue != null) { if (message.DequeueCount >= _maxDequeueCount) { _log.WriteLine("Message has reached MaxDequeueCount of {0}. Moving message to queue '{1}'.", _maxDequeueCount, _poisonQueue.Name); await CopyToPoisonQueueAsync(message, cancellationToken); await DeleteMessageAsync(message, cancellationToken); } else { await ReleaseMessageAsync(message, cancellationToken); } } else { // For queues without a corresponding poison queue, leave the message invisible when processing // fails to prevent a fast infinite loop. // Specifically, don't call ReleaseMessage(message) } } catch (OperationCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (Exception exception) { // Immediately report any unhandled exception from this background task. // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until // Stop is called, which might never happen.) _backgroundExceptionDispatcher.Throw(ExceptionDispatchInfo.Capture(exception)); } }
public async Task EnsureAllStoppedAsync(CancellationToken cancellationToken) { if (_started) { _strategy.Cancel(); await _timer.StopAsync(cancellationToken); _started = false; } }
public async Task StopAsync(CancellationToken cancellationToken) { using (cancellationToken.Register(() => _shutdownCancellationTokenSource.Cancel())) { ThrowIfDisposed(); _timer.Cancel(); await Task.WhenAll(_processing).ConfigureAwait(false); await _timer.StopAsync(cancellationToken).ConfigureAwait(false); } }
public void Dispose_IfStopped_DoesNotThrow() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.Zero); ITaskSeriesTimer product = CreateProductUnderTest(command); product.Start(); product.StopAsync(CancellationToken.None).GetAwaiter().GetResult(); // Act & Assert ExceptionAssert.DoesNotThrow(() => product.Dispose()); }
public void StopAsync_IfDisposed_Throws() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); ITaskSeriesTimer product = CreateProductUnderTest(command); product.Dispose(); CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsObjectDisposed(() => product.StopAsync(cancellationToken)); }
public void StopAsync_IfNotStarted_Throws() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsInvalidOperation(() => product.StopAsync(cancellationToken), "The timer has not yet been started."); } }
private async Task ExecuteWithLoggingAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TraceWriter trace, ILogger logger, IFunctionOutputDefinition outputDefinition, IDictionary <string, ParameterLog> parameterLogCollector, TraceLevel functionTraceLevel, CancellationTokenSource functionCancellationTokenSource) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyDictionary <string, IWatcher> parameterWatchers = null; ITaskSeriesTimer updateParameterLogTimer = null; if (functionTraceLevel >= TraceLevel.Info) { parameterWatchers = CreateParameterWatchers(parameters); IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace, logger); updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _exceptionHandler); } try { await ExecuteWithWatchersAsync(instance, parameters, trace, logger, functionCancellationTokenSource); if (updateParameterLogTimer != null) { // Stop the watches after calling IValueBinder.SetValue (it may do things that should show up in // the watches). // Also, IValueBinder.SetValue could also take a long time (flushing large caches), and so it's // useful to have watches still running. await updateParameterLogTimer.StopAsync(functionCancellationTokenSource.Token); } } finally { if (updateParameterLogTimer != null) { ((IDisposable)updateParameterLogTimer).Dispose(); } if (parameterWatchers != null) { ValueWatcher.AddLogs(parameterWatchers, parameterLogCollector); } } }
public async Task <IDelayedException> TryExecuteAsync(IFunctionInstance instance, CancellationToken cancellationToken) { IDelayedException result; using (ITaskSeriesTimer timer = CreateHeartbeatTimer(_exceptionHandler)) { await _heartbeatCommand.TryExecuteAsync(cancellationToken); timer.Start(); result = await _innerExecutor.TryExecuteAsync(instance, cancellationToken); await timer.StopAsync(cancellationToken); } return(result); }
internal async Task ProcessMessageAsync(QueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { if (!await _queueProcessor.BeginProcessingMessageAsync(message, cancellationToken).ConfigureAwait(false)) { return; } FunctionResult result = null; Action <UpdateReceipt> onUpdateReceipt = updateReceipt => { message = message.Update(updateReceipt); }; using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _exceptionHandler, onUpdateReceipt)) { timer.Start(); result = await _triggerExecutor.ExecuteAsync(message, cancellationToken).ConfigureAwait(false); await timer.StopAsync(cancellationToken).ConfigureAwait(false); } // Use a different cancellation token for shutdown to allow graceful shutdown. // Specifically, don't cancel the completion or update of the message itself during graceful shutdown. // Only cancel completion or update of the message if a non-graceful shutdown is requested via _shutdownCancellationTokenSource. await _queueProcessor.CompleteProcessingMessageAsync(message, result, _shutdownCancellationTokenSource.Token).ConfigureAwait(false); } catch (TaskCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (OperationCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (Exception exception) { // Immediately report any unhandled exception from this background task. // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until // Stop is called, which might never happen.) #pragma warning disable AZC0103 // Do not wait synchronously in asynchronous scope. _exceptionHandler.OnUnhandledExceptionAsync(ExceptionDispatchInfo.Capture(exception)).GetAwaiter().GetResult(); #pragma warning restore AZC0103 // Do not wait synchronously in asynchronous scope. } }
public void StopAsync_WaitsForTaskCompletionToCompleteTask() { // Arrange bool executedOnce = false; bool taskCompleted = false; using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle stopExecuteInFiveMilliseconds = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(async() => { if (executedOnce) { return(new TaskSeriesCommandResult(wait: Task.Delay(0))); } executedOnce = true; Assert.True(executeStarted.Set()); // Guard // Detect the difference between waiting and not waiting, but keep the test execution time fast. await Task.Delay(5); taskCompleted = true; return(new TaskSeriesCommandResult(wait: Task.Delay(0))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard Assert.True(stopExecuteInFiveMilliseconds.Set()); // Guard CancellationToken cancellationToken = CancellationToken.None; // Act Task stopTask = product.StopAsync(cancellationToken); // Assert Assert.NotNull(stopTask); stopTask.GetAwaiter().GetResult(); Assert.True(taskCompleted); } } }
public void StopAsync_TriggersNotExecutingAgain() { // Arrange using (EventWaitHandle executedOnceWaitHandle = new ManualResetEvent(initialState: false)) { ITaskSeriesTimer product = null; Task stop = null; bool executedOnce = false; bool executedTwice = false; ITaskSeriesCommand command = CreateCommand(() => { if (!executedOnce) { stop = product.StopAsync(CancellationToken.None); Assert.True(executedOnceWaitHandle.Set()); // Guard executedOnce = true; return(new TaskSeriesCommandResult(wait: Task.Delay(0))); } else { executedTwice = true; return(new TaskSeriesCommandResult(wait: Task.Delay(0))); } }); using (product = CreateProductUnderTest(command)) { product.Start(); // Act bool executed = executedOnceWaitHandle.WaitOne(1000); // Assert Assert.True(executed); // Guard Assert.NotNull(stop); stop.GetAwaiter().GetResult(); Assert.False(executedTwice); } } }
public void StopAsync_WhenExecuteTaskCompletesFaulted_DoesNotThrowOrFault() { // Arrange bool executedOnce = false; using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle stopExecuteInFiveMilliseconds = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { if (executedOnce) { return(Task.FromResult(new TaskSeriesCommandResult(wait: Task.Delay(0)))); } executedOnce = true; Assert.True(executeStarted.Set()); // Guard TaskCompletionSource <TaskSeriesCommandResult> taskSource = new TaskCompletionSource <TaskSeriesCommandResult>(); taskSource.SetException(new InvalidOperationException()); return(taskSource.Task); }); IWebJobsExceptionHandler ignoreDispatcher = CreateIgnoreBackgroundExceptionDispatcher(); using (ITaskSeriesTimer product = CreateProductUnderTest(command, ignoreDispatcher)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard CancellationToken cancellationToken = CancellationToken.None; // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); task.GetAwaiter().GetResult(); } } }
public void StopAsync_DoesNotWaitForInitialWaitToCompleteTask() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.Zero); using (ITaskSeriesTimer product = CreateProductUnderTest(command, initialWait: Task.Delay(2000))) { product.Start(); // Wait for the background thread to enter the initialWait. Thread.Sleep(5); CancellationToken cancellationToken = CancellationToken.None; // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); Assert.True(task.WaitUntilCompleted(1000)); Assert.True(task.IsCompleted); } }
private async Task <string> ExecuteWithLogMessageAsync(IFunctionInstance instance, FunctionStartedMessage message, IDictionary <string, ParameterLog> parameterLogCollector, CancellationToken cancellationToken) { string startedMessageId; // Create the console output writer IFunctionOutputDefinition outputDefinition = await _functionOutputLogger.CreateAsync(instance, cancellationToken); // Create a linked token source that will allow us to signal function cancellation // (e.g. Based on TimeoutAttribute, etc.) CancellationTokenSource functionCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); using (IFunctionOutput outputLog = await outputDefinition.CreateOutputAsync(cancellationToken)) using (ITaskSeriesTimer updateOutputLogTimer = StartOutputTimer(outputLog.UpdateCommand, _backgroundExceptionDispatcher)) using (functionCancellationTokenSource) { // We create a new composite trace writer that will also forward // output to the function output log (in addition to console, user TraceWriter, etc.). TraceWriter traceWriter = new CompositeTraceWriter(_trace, outputLog.Output); FunctionBindingContext functionContext = new FunctionBindingContext(instance.Id, functionCancellationTokenSource.Token, traceWriter); // Must bind before logging (bound invoke string is included in log message). IReadOnlyDictionary <string, IValueProvider> parameters = await instance.BindingSource.BindAsync(new ValueBindingContext(functionContext, cancellationToken)); ExceptionDispatchInfo exceptionInfo; using (ValueProviderDisposable.Create(parameters)) { startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken); try { await ExecuteWithOutputLogsAsync(instance, parameters, traceWriter, outputDefinition, parameterLogCollector, functionCancellationTokenSource); exceptionInfo = null; } catch (OperationCanceledException exception) { exceptionInfo = ExceptionDispatchInfo.Capture(exception); } catch (Exception exception) { string errorMessage = string.Format("Exception while executing function: {0}", instance.FunctionDescriptor.ShortName); FunctionInvocationException functionException = new FunctionInvocationException(errorMessage, instance.Id, instance.FunctionDescriptor.FullName, exception); traceWriter.Error(errorMessage, functionException, TraceSource.Execution); exceptionInfo = ExceptionDispatchInfo.Capture(functionException); } } if (exceptionInfo == null && updateOutputLogTimer != null) { await updateOutputLogTimer.StopAsync(cancellationToken); } // after all execution is complete, flush the TraceWriter traceWriter.Flush(); // We save the exception info rather than doing throw; above to ensure we always write console output, // even if the function fails or was canceled. await outputLog.SaveAndCloseAsync(cancellationToken); if (exceptionInfo != null) { // release any held singleton lock immediately SingletonLock singleton = null; if (TryGetSingletonLock(parameters, out singleton) && singleton.IsHeld) { await singleton.ReleaseAsync(cancellationToken); } exceptionInfo.Throw(); } return(startedMessageId); } }
private async Task <string> ExecuteWithLoggingAsync(IFunctionInstance instance, FunctionStartedMessage message, FunctionInstanceLogEntry fastItem, IDictionary <string, ParameterLog> parameterLogCollector, TraceLevel functionTraceLevel, CancellationToken cancellationToken) { IFunctionOutputDefinition outputDefinition = null; IFunctionOutput outputLog = null; ITaskSeriesTimer updateOutputLogTimer = null; TextWriter functionOutputTextWriter = null; Func <Task> initializeOutputAsync = async() => { outputDefinition = await _functionOutputLogger.CreateAsync(instance, cancellationToken); outputLog = outputDefinition.CreateOutput(); functionOutputTextWriter = outputLog.Output; updateOutputLogTimer = StartOutputTimer(outputLog.UpdateCommand, _exceptionHandler); }; if (functionTraceLevel >= TraceLevel.Info) { await initializeOutputAsync(); } try { // Create a linked token source that will allow us to signal function cancellation // (e.g. Based on TimeoutAttribute, etc.) CancellationTokenSource functionCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); using (functionCancellationTokenSource) { // We create a new composite trace writer that will also forward // output to the function output log (in addition to console, user TraceWriter, etc.). TraceWriter traceWriter = new CompositeTraceWriter(_trace, functionOutputTextWriter, functionTraceLevel); // Must bind before logging (bound invoke string is included in log message). FunctionBindingContext functionContext = new FunctionBindingContext(instance.Id, functionCancellationTokenSource.Token, traceWriter); var valueBindingContext = new ValueBindingContext(functionContext, cancellationToken); var parameters = await instance.BindingSource.BindAsync(valueBindingContext); Exception invocationException = null; ExceptionDispatchInfo exceptionInfo = null; string startedMessageId = null; using (ValueProviderDisposable.Create(parameters)) { if (functionTraceLevel >= TraceLevel.Info) { startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken); } if (_fastLogger != null) { // Log started fastItem.Arguments = message.Arguments; await _fastLogger.AddAsync(fastItem); } try { await ExecuteWithLoggingAsync(instance, parameters, traceWriter, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource); } catch (Exception ex) { invocationException = ex; } } if (invocationException != null) { if (outputDefinition == null) { // In error cases, even if logging is disabled for this function, we want to force // log errors. So we must delay initialize logging here await initializeOutputAsync(); startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken); } // In the event of cancellation or timeout, we use the original exception without additional logging. if (invocationException is OperationCanceledException || invocationException is FunctionTimeoutException) { exceptionInfo = ExceptionDispatchInfo.Capture(invocationException); } else { string errorMessage = string.Format("Exception while executing function: {0}", instance.FunctionDescriptor.ShortName); FunctionInvocationException fex = new FunctionInvocationException(errorMessage, instance.Id, instance.FunctionDescriptor.FullName, invocationException); traceWriter.Error(errorMessage, fex, TraceSource.Execution); exceptionInfo = ExceptionDispatchInfo.Capture(fex); } } if (exceptionInfo == null && updateOutputLogTimer != null) { await updateOutputLogTimer.StopAsync(cancellationToken); } // after all execution is complete, flush the TraceWriter traceWriter.Flush(); // We save the exception info above rather than throwing to ensure we always write // console output even if the function fails or was canceled. if (outputLog != null) { await outputLog.SaveAndCloseAsync(fastItem, cancellationToken); } if (exceptionInfo != null) { // release any held singleton lock immediately SingletonLock singleton = null; if (TryGetSingletonLock(parameters, out singleton) && singleton.IsHeld) { await singleton.ReleaseAsync(cancellationToken); } exceptionInfo.Throw(); } return(startedMessageId); } } finally { if (outputLog != null) { ((IDisposable)outputLog).Dispose(); } if (updateOutputLogTimer != null) { ((IDisposable)updateOutputLogTimer).Dispose(); } } }
public Task StopAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); return(_timer.StopAsync(cancellationToken)); }
private async Task <string> ExecuteWithLogMessageAsync(IFunctionInstance instance, FunctionStartedMessage message, IDictionary <string, ParameterLog> parameterLogCollector, CancellationToken cancellationToken) { string startedMessageId; // Create the console output writer IFunctionOutputDefinition outputDefinition = await _functionOutputLogger.CreateAsync(instance, cancellationToken); using (IFunctionOutput outputLog = await outputDefinition.CreateOutputAsync(cancellationToken)) using (ITaskSeriesTimer updateOutputLogTimer = StartOutputTimer(outputLog.UpdateCommand, _backgroundExceptionDispatcher)) { TextWriter consoleOutput = outputLog.Output; FunctionBindingContext functionContext = new FunctionBindingContext(instance.Id, cancellationToken, consoleOutput); // Must bind before logging (bound invoke string is included in log message). IReadOnlyDictionary <string, IValueProvider> parameters = await instance.BindingSource.BindAsync(new ValueBindingContext(functionContext, cancellationToken)); ExceptionDispatchInfo exceptionInfo; using (ValueProviderDisposable.Create(parameters)) { startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken); try { await ExecuteWithOutputLogsAsync(instance, parameters, consoleOutput, outputDefinition, parameterLogCollector, cancellationToken); exceptionInfo = null; } catch (OperationCanceledException exception) { exceptionInfo = ExceptionDispatchInfo.Capture(exception); } catch (Exception exception) { consoleOutput.WriteLine("--------"); consoleOutput.WriteLine("Exception while executing:"); consoleOutput.Write(exception.ToDetails()); exceptionInfo = ExceptionDispatchInfo.Capture(exception); } } if (exceptionInfo == null && updateOutputLogTimer != null) { await updateOutputLogTimer.StopAsync(cancellationToken); } // We save the exception info rather than doing throw; above to ensure we always write console output, // even if the function fails or was canceled. await outputLog.SaveAndCloseAsync(cancellationToken); if (exceptionInfo != null) { exceptionInfo.Throw(); } return(startedMessageId); } }
public async Task StopAsync(CancellationToken cancellationToken) { await _timer.StopAsync(cancellationToken); await _innerListener.StopAsync(cancellationToken); }