diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs index d860312da271..ec4335d5f534 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs @@ -20,9 +20,25 @@ internal sealed class HostFactoryResolver public const string BuildWebHost = nameof(BuildWebHost); public const string CreateWebHostBuilder = nameof(CreateWebHostBuilder); public const string CreateHostBuilder = nameof(CreateHostBuilder); + private const string TimeoutEnvironmentKey = "DOTNET_HOST_FACTORY_RESOLVER_DEFAULT_TIMEOUT_IN_SECONDS"; // The amount of time we wait for the diagnostic source events to fire - private static readonly TimeSpan s_defaultWaitTimeout = Debugger.IsAttached ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(5); + private static readonly TimeSpan s_defaultWaitTimeout = SetupDefaultTimout(); + + private static TimeSpan SetupDefaultTimout() + { + if (Debugger.IsAttached) + { + return Timeout.InfiniteTimeSpan; + } + + if (uint.TryParse(Environment.GetEnvironmentVariable(TimeoutEnvironmentKey), out uint timeoutInSeconds)) + { + return TimeSpan.FromSeconds((int)timeoutInSeconds); + } + + return TimeSpan.FromMinutes(5); + } public static Func? ResolveWebHostFactory(Assembly assembly) { @@ -229,7 +245,7 @@ public object CreateHost() // Try to set an exception if the entry point returns gracefully, this will force // build to throw - _hostTcs.TrySetException(new InvalidOperationException("Unable to build IHost")); + _hostTcs.TrySetException(new InvalidOperationException("The entry point exited without ever building an IHost.")); } catch (TargetInvocationException tie) when (tie.InnerException is StopTheHostException) { @@ -268,7 +284,7 @@ public object CreateHost() // Wait before throwing an exception if (!_hostTcs.Task.Wait(_waitTimeout)) { - throw new InvalidOperationException("Unable to build IHost"); + throw new InvalidOperationException($"Timed out waiting for the entry point to build the IHost after {s_defaultWaitTimeout}. This timeout can be modified using the '{TimeoutEnvironmentKey}' environment variable."); } } catch (AggregateException) when (_hostTcs.Task.IsCompleted) diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj index 31cd86025ee0..90718d135b40 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj @@ -9,6 +9,8 @@ true true + true + 1 Internal package for sharing Microsoft.Extensions.Hosting.HostFactoryResolver type. diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/HostFactoryResolverTests.cs b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/HostFactoryResolverTests.cs index 0e353f7f8cb7..d6cf530c94e9 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/HostFactoryResolverTests.cs +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/HostFactoryResolverTests.cs @@ -253,6 +253,16 @@ public void TopLevelStatements() Assert.IsAssignableFrom(factory(Array.Empty())); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public void TopLevelStatementsTestsTimeout() + { + var assembly = Assembly.Load("TopLevelStatementsTestsTimeout"); + var factory = HostFactoryResolver.ResolveServiceProviderFactory(assembly, s_WaitTimeout); + + Assert.NotNull(factory); + Assert.Throws(() => factory(Array.Empty())); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void ApplicationNameSetFromAgrument() { diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/Microsoft.Extensions.HostFactoryResolver.Tests.csproj b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/Microsoft.Extensions.HostFactoryResolver.Tests.csproj index bae78e8e4e18..fe811f1fe1b5 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/Microsoft.Extensions.HostFactoryResolver.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/Microsoft.Extensions.HostFactoryResolver.Tests.csproj @@ -28,6 +28,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/Program.cs b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/Program.cs new file mode 100644 index 000000000000..f4c01882987e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/Program.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using Microsoft.Extensions.Hosting; + +var hostBuilder = new HostBuilder(); +Thread.Sleep(TimeSpan.FromSeconds(30)); +hostBuilder.Build(); \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/TopLevelStatementsTestsTimeout.csproj b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/TopLevelStatementsTestsTimeout.csproj new file mode 100644 index 000000000000..716b25a87281 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/tests/TopLevelStatementsTestsTimeout/TopLevelStatementsTestsTimeout.csproj @@ -0,0 +1,13 @@ + + + + $(NetCoreAppCurrent);net461 + true + Exe + + + + + + +