public void TimeoutIsEvaluatedInSeconds() { var attrib = new TimeoutAttribute(120); Assert.AreEqual(120, attrib.TimeoutSeconds); Assert.AreEqual(TimeSpan.FromSeconds(120), attrib.Timeout); }
public FunctionIndexProvider(ITypeLocator typeLocator, ITriggerBindingProvider triggerBindingProvider, CompositeBindingProvider bindingProviderFactory, IJobActivator activator, IFunctionExecutor executor, IExtensionRegistry extensions, SingletonManager singletonManager, ILoggerFactory loggerFactory, SharedQueueHandler sharedQueue, IOptions <JobHostFunctionTimeoutOptions> timeoutOptions, IOptions <JobHostOptions> hostOptions) { _typeLocator = typeLocator ?? throw new ArgumentNullException(nameof(typeLocator)); _triggerBindingProvider = triggerBindingProvider ?? throw new ArgumentNullException(nameof(triggerBindingProvider)); _bindingProviderFactory = bindingProviderFactory ?? throw new ArgumentNullException(nameof(bindingProviderFactory)); _activator = activator ?? throw new ArgumentNullException(nameof(activator)); _executor = executor ?? throw new ArgumentNullException(nameof(executor)); _extensions = extensions ?? throw new ArgumentNullException(nameof(extensions)); _singletonManager = singletonManager ?? throw new ArgumentNullException(nameof(singletonManager)); _sharedQueue = sharedQueue ?? throw new ArgumentNullException(nameof(sharedQueue)); _loggerFactory = loggerFactory; _defaultTimeout = timeoutOptions.Value.ToAttribute(); _allowPartialHostStartup = hostOptions.Value.AllowPartialHostStartup; }
private void RunOnFunctionTimeoutTest(bool isDebugging, string expectedMessage) { System.Timers.Timer timer = new System.Timers.Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); timer.Start(); Assert.True(timer.Enabled); Assert.False(_cancellationTokenSource.IsCancellationRequested); MethodInfo method = typeof(Functions).GetMethod("MethodLevel", BindingFlags.Static | BindingFlags.Public); TimeoutAttribute attribute = method.GetCustomAttribute <TimeoutAttribute>(); _descriptor = FunctionIndexer.FromMethod(method); Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4"); bool timeoutWhileDebugging = false; TestLogger logger = new TestLogger("Tests.FunctionExecutor"); FunctionExecutor.OnFunctionTimeout(timer, _descriptor, instanceId, attribute.Timeout, timeoutWhileDebugging, logger, _cancellationTokenSource, () => isDebugging); Assert.False(timer.Enabled); Assert.NotEqual(isDebugging, _cancellationTokenSource.IsCancellationRequested); string message = string.Format("Timeout value of 00:01:00 exceeded by function 'Functions.MethodLevel' (Id: 'b2d1dd72-80e2-412b-a22e-3b4558f378b4'). {0}", expectedMessage); // verify ILogger LogMessage log = logger.LogMessages.Single(); Assert.Equal(LogLevel.Error, log.Level); Assert.Equal(message, log.FormattedMessage); }
public async Task OnRequestAsync_Parameter_Timespan_Test() { var apiAction = new ApiActionDescriptor(typeof(ITestApi).GetMethod("PostAsync")); var context = new TestRequestContext(apiAction, 5); var attr = new TimeoutAttribute(); var parameterContext = new ApiParameterContext(context, 0); await attr.OnRequestAsync(parameterContext, () => Task.CompletedTask); await Task.Delay(20); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); context.Arguments[0] = Guid.NewGuid(); await Assert.ThrowsAsync <HttpApiInvalidOperationException>(() => attr.OnRequestAsync(parameterContext, () => Task.CompletedTask)); context.Arguments[0] = null; await attr.OnRequestAsync(parameterContext, () => Task.CompletedTask); Assert.True(context.CancellationTokens.Count == 1); }
private void RunOnFunctionTimeoutTest(bool isDebugging, string expectedMessage) { System.Timers.Timer timer = new System.Timers.Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); timer.Start(); Assert.True(timer.Enabled); Assert.False(_cancellationTokenSource.IsCancellationRequested); MethodInfo method = typeof(Functions).GetMethod("MethodLevel", BindingFlags.Static | BindingFlags.Public); TimeoutAttribute attribute = method.GetCustomAttribute <TimeoutAttribute>(); Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4"); bool timeoutWhileDebugging = false; FunctionExecutor.OnFunctionTimeout(timer, method, instanceId, attribute.Timeout, timeoutWhileDebugging, _traceWriter, _cancellationTokenSource, () => isDebugging); Assert.False(timer.Enabled); Assert.NotEqual(isDebugging, _cancellationTokenSource.IsCancellationRequested); TraceEvent trace = _traceWriter.Traces[0]; Assert.Equal(TraceLevel.Error, trace.Level); Assert.Equal(TraceSource.Execution, trace.Source); string message = string.Format("Timeout value of 00:01:00 exceeded by function 'Functions.MethodLevel' (Id: 'b2d1dd72-80e2-412b-a22e-3b4558f378b4'). {0}", expectedMessage); Assert.Equal(message, trace.Message); }
public FunctionIndexProvider(ITypeLocator typeLocator, ITriggerBindingProvider triggerBindingProvider, IBindingProvider bindingProvider, IJobActivator activator, IFunctionExecutor executor, IExtensionRegistry extensions, SingletonManager singletonManager, ILoggerFactory loggerFactory, SharedQueueHandler sharedQueue, TimeoutAttribute defaultTimeout, bool allowPartialHostStartup = false) { _typeLocator = typeLocator ?? throw new ArgumentNullException(nameof(typeLocator)); _triggerBindingProvider = triggerBindingProvider ?? throw new ArgumentNullException(nameof(triggerBindingProvider)); _bindingProvider = bindingProvider ?? throw new ArgumentNullException(nameof(bindingProvider)); _activator = activator ?? throw new ArgumentNullException(nameof(activator)); _executor = executor ?? throw new ArgumentNullException(nameof(executor)); _extensions = extensions ?? throw new ArgumentNullException(nameof(extensions)); _singletonManager = singletonManager ?? throw new ArgumentNullException(nameof(singletonManager)); _sharedQueue = sharedQueue ?? throw new ArgumentNullException(nameof(sharedQueue)); _loggerFactory = loggerFactory; _defaultTimeout = defaultTimeout; _allowPartialHostStartup = allowPartialHostStartup; }
public async Task BeforeRequestAsync_Parameter_Timespan_Test() { var context = new TestActionContext( httpApi: null, httpApiConfig: new HttpApiConfig(), apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync"))); IApiParameterAttribute attr = new TimeoutAttribute(); var parameter = context.ApiActionDescriptor.Parameters[0].Clone(TimeSpan.FromMilliseconds(5)); await attr.BeforeRequestAsync(context, parameter); await Task.Delay(10); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); parameter = context.ApiActionDescriptor.Parameters[0].Clone(Guid.NewGuid()); await Assert.ThrowsAsync <HttpApiConfigException>(() => attr.BeforeRequestAsync(context, parameter)); parameter = context.ApiActionDescriptor.Parameters[0].Clone(null); await attr.BeforeRequestAsync(context, parameter); Assert.True(context.CancellationTokens.Count == 1); }
public void ZeroTimeoutMeansInfinite() { var attrib = new TimeoutAttribute(0); Assert.AreEqual(0, attrib.TimeoutSeconds); Assert.IsNull(attrib.Timeout); }
public void TimeoutAttribute() { var attr = new TimeoutAttribute(50) as IApplyToContext; attr.ApplyToContext(_context); Assert.That(_context.TestCaseTimeout, Is.EqualTo(50)); }
public void StartFunctionTimeout_NoTimeout_ReturnsNull() { TimeoutAttribute timeoutAttribute = null; System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(null, timeoutAttribute, _cancellationTokenSource, _traceWriter); Assert.Null(timer); }
public FunctionIndexer( ITriggerBindingProvider triggerBindingProvider, IBindingProvider bindingProvider, IJobActivator activator, IFunctionExecutor executor, IExtensionRegistry extensions, SingletonManager singletonManager, ILoggerFactory loggerFactory, INameResolver nameResolver = null, SharedQueueHandler sharedQueue = null, TimeoutAttribute defaultTimeout = null, bool allowPartialHostStartup = false) { if (triggerBindingProvider == null) { throw new ArgumentNullException("triggerBindingProvider"); } if (bindingProvider == null) { throw new ArgumentNullException("bindingProvider"); } if (activator == null) { throw new ArgumentNullException("activator"); } if (executor == null) { throw new ArgumentNullException("executor"); } if (extensions == null) { throw new ArgumentNullException("extensions"); } if (singletonManager == null) { throw new ArgumentNullException("singletonManager"); } _triggerBindingProvider = triggerBindingProvider; _bindingProvider = bindingProvider; _activator = activator; _executor = executor; _singletonManager = singletonManager; _jobAttributeAssemblies = GetJobAttributeAssemblies(extensions); _nameResolver = nameResolver; _logger = loggerFactory?.CreateLogger(LogCategories.Startup); _sharedQueue = sharedQueue; _defaultTimeout = defaultTimeout; _allowPartialHostStartup = allowPartialHostStartup; }
public async Task OnRequestAsync() { var apiAction = new ApiActionDescriptor(typeof(ITestApi).GetMethod("PostAsync")); var context = new TestRequestContext(apiAction, 10); var attr = new TimeoutAttribute(50); await attr.OnRequestAsync(context); await Task.Delay(100); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); }
public void StartFunctionTimeout_GlobalTimeout_CreatesExpectedTimer() { MethodInfo method = GetType().GetMethod("GlobalLevel", BindingFlags.Static | BindingFlags.Public); _descriptor.Method = method; TimeoutAttribute attribute = method.GetCustomAttribute <TimeoutAttribute>(); System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, _globalFunctionTimeout, _cancellationTokenSource, _traceWriter); Assert.True(timer.Enabled); Assert.Equal(_globalFunctionTimeout.TotalMilliseconds, timer.Interval); _mockFunctionInstance.VerifyAll(); }
public async Task BeforeRequestAsyncTest() { var context = new ApiActionContext { HttpApiConfig = new HttpApiConfig(), RequestMessage = new HttpApiRequestMessage(), ApiActionDescriptor = ApiDescriptorCache.GetApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")) }; var attr = new TimeoutAttribute(5000); await attr.BeforeRequestAsync(context); Assert.True(context.RequestMessage.Timeout == TimeSpan.FromSeconds(5)); }
public async Task OnRequestAsync_Parameter_Double_Test() { var apiAction = new ApiActionDescriptor(typeof(ITestApi).GetMethod("PostAsync")); var context = new TestRequestContext(apiAction, 1); var attr = new TimeoutAttribute(); var parameterContext = new ApiParameterContext(context, 0); await attr.OnRequestAsync(parameterContext, () => Task.CompletedTask); await Task.Delay(20); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); }
public void Constructor_DefaultsProperties() { var timeout = new TimeoutAttribute("00:00:25"); Assert.Equal(TimeSpan.FromSeconds(25), timeout.Timeout); Assert.Equal(TimeSpan.FromSeconds(2), timeout.GracePeriod); Assert.False(timeout.TimeoutWhileDebugging); Assert.False(timeout.ThrowOnTimeout); timeout = new TimeoutAttribute("00:05:00", "00:00:30"); Assert.Equal(TimeSpan.FromMinutes(5), timeout.Timeout); Assert.Equal(TimeSpan.FromSeconds(30), timeout.GracePeriod); Assert.False(timeout.TimeoutWhileDebugging); Assert.False(timeout.ThrowOnTimeout); }
public async Task BeforeRequestAsyncTest() { var context = new TestActionContext( httpApi: null, httpApiConfig: new HttpApiConfig(), apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync"))); var attr = new TimeoutAttribute(50); await attr.BeforeRequestAsync(context); await Task.Delay(100); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); }
public void StartFunctionTimeout_NoCancellationTokenParameter_ThrowOnTimeoutFalse_ReturnsNull() { MethodInfo method = typeof(Functions).GetMethod("NoCancellationTokenParameter", BindingFlags.Static | BindingFlags.Public); _descriptor.Method = method; TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute <TimeoutAttribute>(); attribute.ThrowOnTimeout = false; System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter); Assert.Null(timer); _mockFunctionInstance.VerifyAll(); }
internal static async Task HandleExceptionAsync(TimeoutAttribute timeout, ExceptionDispatchInfo exceptionInfo, IWebJobsExceptionHandler exceptionHandler) { if (exceptionInfo.SourceException == null) { return; } Exception exception = exceptionInfo.SourceException; if (exception.IsTimeout()) { await exceptionHandler.OnTimeoutExceptionAsync(exceptionInfo, timeout.GracePeriod); } else if (exception.IsFatal()) { await exceptionHandler.OnUnhandledExceptionAsync(exceptionInfo); } }
public async Task BeforeRequestAsync_Parameter_Double_Test() { var context = new TestActionContext( httpApi: null, httpApiConfig: new HttpApiConfig(), apiActionDescriptor: new ApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync"))); IApiParameterAttribute attr = new TimeoutAttribute(); var parameter = context.ApiActionDescriptor.Parameters[0].Clone(10); await attr.BeforeRequestAsync(context, parameter); await Task.Delay(20); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); }
public async Task BeforeRequestAsyncTest() { var context = new ApiActionContext { HttpApiConfig = new HttpApiConfig(), RequestMessage = new HttpApiRequestMessage(), ApiActionDescriptor = ApiDescriptorCache.GetApiActionDescriptor(typeof(IMyApi).GetMethod("PostAsync")) }; var attr = new TimeoutAttribute(50); await attr.BeforeRequestAsync(context); await Task.Delay(100); var canceled = context.CancellationTokens[0].IsCancellationRequested; Assert.True(canceled); }
internal static async Task HandleExceptionAsync(MethodInfo method, ExceptionDispatchInfo exceptionInfo, IWebJobsExceptionHandler exceptionHandler) { if (exceptionInfo.SourceException == null) { return; } Exception exception = exceptionInfo.SourceException; if (exception.IsTimeout()) { TimeoutAttribute timeoutAttribute = TypeUtility.GetHierarchicalAttributeOrNull <TimeoutAttribute>(method); await exceptionHandler.OnTimeoutExceptionAsync(exceptionInfo, timeoutAttribute.GracePeriod); } else if (exception.IsFatal()) { await exceptionHandler.OnUnhandledExceptionAsync(exceptionInfo); } }
public void StartFunctionTimeout_ClassLevelTimeout_CreatesExpectedTimer() { MethodInfo method = typeof(Functions).GetMethod("ClassLevel", BindingFlags.Static | BindingFlags.Public); _descriptor.Method = method; // we need to set up the Id so that when the timer fires it doesn't throw, but since this is Strict, we need to access it first. _mockFunctionInstance.SetupGet(p => p.Id).Returns(Guid.Empty); Assert.NotNull(_mockFunctionInstance.Object.Id); TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute <TimeoutAttribute>(); System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter); Assert.True(timer.Enabled); Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval); _mockFunctionInstance.VerifyAll(); }
internal static System.Timers.Timer StartFunctionTimeout(IFunctionInstance instance, TimeSpan?globalTimeout, CancellationTokenSource cancellationTokenSource, TraceWriter trace) { MethodInfo method = instance.FunctionDescriptor.Method; if (!method.GetParameters().Any(p => p.ParameterType == typeof(CancellationToken))) { // function doesn't bind to the CancellationToken, so no point in setting // up the cancellation timer return(null); } // first see if there is a Timeout applied to the method or class TimeSpan? timeout = globalTimeout; TimeoutAttribute timeoutAttribute = TypeUtility.GetHierarchicalAttributeOrNull <TimeoutAttribute>(method); if (timeoutAttribute != null) { timeout = timeoutAttribute.Timeout; } if (timeout != null) { // Create a Timer that will cancel the token source when it fires. We're using our // own Timer (rather than CancellationToken.CancelAfter) so we can write a log entry // before cancellation occurs. var timer = new System.Timers.Timer(timeout.Value.TotalMilliseconds) { AutoReset = false }; timer.Elapsed += (o, e) => { OnFunctionTimeout(timer, method, instance.Id, timeout.Value, trace, cancellationTokenSource); }; timer.Start(); return(timer); } return(null); }
private void HasValidTimeoutAttribute() { StringBuilder sb = new StringBuilder(); foreach (MethodInfo methodInfo in this.TestMethods) { TimeoutAttribute timeout = methodInfo.GetCustomAttributes(typeof(TimeoutAttribute), false).Cast <TimeoutAttribute>().FirstOrDefault(); if (timeout == null) { sb.AppendLine(string.Format(" {0}: should have [Timeout].", methodInfo.Name)); } else if (timeout.Timeout <= 0 || timeout.Timeout > (TimeoutConstant.ExtendedTimeout)) { sb.AppendLine(string.Format(" {0}: [Timeout({1})] must be > 0 and < {2} milliseconds.", methodInfo.Name, timeout.Timeout, TimeoutConstant.ExtendedTimeout)); } } if (sb.Length != 0) { Assert.Fail(string.Format("{0}these test methods have incorrect [Timeout] attributes:\r\n{1}.", testErrorPrefix, sb.ToString())); } }
private TestMethodUnit(MethodInfo testCase) { m_testCase = testCase; name = testCase.Name; m_tagAttrs = new List <string>(); foreach (Attribute attr in testCase.GetCustomAttributes(true)) { if (null != (attr as TestMethodAttribute)) { m_attr = (TestMethodAttribute)attr; } if (null != (attr as TimeoutAttribute)) { m_timeoutAttr = (TimeoutAttribute)attr; } if (null != (attr as TestCategoryAttribute)) { m_tagAttrs.Add(((TestCategoryAttribute)attr).TestCategories[0]); } if (null != (attr as IgnoreAttribute)) { m_ignoreAttr = (IgnoreAttribute)attr; } } //default is all enabled if not ignored if (Ignore) { enable = false; } else { enable = true; } }
// Expose internally for testing purposes internal static FunctionDescriptor FromMethod( MethodInfo method, IJobActivator jobActivator = null, INameResolver nameResolver = null, TimeoutAttribute defaultTimeout = null) { var disabled = HostListenerFactory.IsDisabled(method, nameResolver, jobActivator); bool hasCancellationToken = method.GetParameters().Any(p => p.ParameterType == typeof(CancellationToken)); string logName = method.Name; string shortName = method.GetShortName(); FunctionNameAttribute nameAttribute = method.GetCustomAttribute <FunctionNameAttribute>(); if (nameAttribute != null) { logName = nameAttribute.Name; shortName = logName; if (!FunctionNameAttribute.FunctionNameValidationRegex.IsMatch(logName)) { throw new InvalidOperationException(string.Format("'{0}' is not a valid function name.", logName)); } } return(new FunctionDescriptor { Id = method.GetFullName(), LogName = logName, FullName = method.GetFullName(), ShortName = shortName, IsDisabled = disabled, HasCancellationToken = hasCancellationToken, TimeoutAttribute = TypeUtility.GetHierarchicalAttributeOrNull <TimeoutAttribute>(method) ?? defaultTimeout, SingletonAttributes = method.GetCustomAttributes <SingletonAttribute>(), MethodLevelFilters = method.GetCustomAttributes().OfType <IFunctionFilter>(), ClassLevelFilters = method.DeclaringType.GetCustomAttributes().OfType <IFunctionFilter>() }); }
public FunctionIndexer( ITriggerBindingProvider triggerBindingProvider, IBindingProvider bindingProvider, IJobActivator activator, IFunctionExecutor executor, SingletonManager singletonManager, ILoggerFactory loggerFactory, INameResolver nameResolver = null, SharedQueueHandler sharedQueue = null, TimeoutAttribute defaultTimeout = null, bool allowPartialHostStartup = false) { _triggerBindingProvider = triggerBindingProvider ?? throw new ArgumentNullException(nameof(triggerBindingProvider)); _bindingProvider = bindingProvider ?? throw new ArgumentNullException(nameof(bindingProvider)); _activator = activator ?? throw new ArgumentNullException(nameof(activator)); _executor = executor ?? throw new ArgumentNullException(nameof(executor)); _singletonManager = singletonManager ?? throw new ArgumentNullException(nameof(singletonManager)); _nameResolver = nameResolver; _logger = loggerFactory?.CreateLogger(LogCategories.Startup); _sharedQueue = sharedQueue; _defaultTimeout = defaultTimeout; _allowPartialHostStartup = allowPartialHostStartup; }
public void OnFunctionTimeout_PerformsExpectedOperations() { System.Timers.Timer timer = new System.Timers.Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); timer.Start(); Assert.True(timer.Enabled); Assert.False(_cancellationTokenSource.IsCancellationRequested); MethodInfo method = typeof(Functions).GetMethod("MethodLevel", BindingFlags.Static | BindingFlags.Public); TimeoutAttribute attribute = method.GetCustomAttribute <TimeoutAttribute>(); Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4"); FunctionExecutor.OnFunctionTimeout(timer, method, instanceId, attribute.Timeout, _traceWriter, _cancellationTokenSource); Assert.False(timer.Enabled); Assert.True(_cancellationTokenSource.IsCancellationRequested); TraceEvent trace = _traceWriter.Traces[0]; Assert.Equal(TraceLevel.Error, trace.Level); Assert.Equal(TraceSource.Execution, trace.Source); Assert.Equal("Timeout value of 00:01:00 exceeded by function 'Functions.MethodLevel' (Id: 'b2d1dd72-80e2-412b-a22e-3b4558f378b4'). Initiating cancellation.", trace.Message); }
internal static async Task ExecuteWithWatchersAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TraceWriter traceWriter, CancellationTokenSource functionCancellationTokenSource) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyList <string> parameterNames = invoker.ParameterNames; IDelayedException delayedBindingException; object[] invokeParameters = PrepareParameters(parameterNames, parameters, out delayedBindingException); if (delayedBindingException != null) { // This is done inside a watcher context so that each binding error is publish next to the binding in // the parameter status log. delayedBindingException.Throw(); } // if the function is a Singleton, aquire the lock SingletonLock singleton = null; if (TryGetSingletonLock(parameters, out singleton)) { await singleton.AcquireAsync(functionCancellationTokenSource.Token); } // Create a source specifically for timeouts using (CancellationTokenSource timeoutTokenSource = new CancellationTokenSource()) { MethodInfo method = instance.FunctionDescriptor.Method; TimeoutAttribute timeoutAttribute = TypeUtility.GetHierarchicalAttributeOrNull <TimeoutAttribute>(method); bool throwOnTimeout = timeoutAttribute == null ? false : timeoutAttribute.ThrowOnTimeout; var timer = StartFunctionTimeout(instance, timeoutAttribute, timeoutTokenSource, traceWriter); TimeSpan timerInterval = timer == null ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(timer.Interval); try { await InvokeAsync(invoker, invokeParameters, timeoutTokenSource, functionCancellationTokenSource, throwOnTimeout, timerInterval, instance); } finally { if (timer != null) { timer.Stop(); timer.Dispose(); } } } // Process any out parameters and persist any pending values. // Ensure IValueBinder.SetValue is called in BindStepOrder. This ordering is particularly important for // ensuring queue outputs occur last. That way, all other function side-effects are guaranteed to have // occurred by the time messages are enqueued. string[] parameterNamesInBindOrder = SortParameterNamesInStepOrder(parameters); foreach (string name in parameterNamesInBindOrder) { IValueProvider provider = parameters[name]; IValueBinder binder = provider as IValueBinder; if (binder != null) { object argument = invokeParameters[GetParameterIndex(parameterNames, name)]; try { // This could do complex things that may fail. Catch the exception. await binder.SetValueAsync(argument, functionCancellationTokenSource.Token); } catch (OperationCanceledException) { throw; } catch (Exception exception) { string message = String.Format(CultureInfo.InvariantCulture, "Error while handling parameter {0} after function returned:", name); throw new InvalidOperationException(message, exception); } } } if (singleton != null) { await singleton.ReleaseAsync(functionCancellationTokenSource.Token); } }