IHostedService with Monitor produces System.OperationCanceledException
See original GitHub issueDescribe the bug
IHostedService
with Monitor
produces System.OperationCanceledException
on some occasions but not on all occasions on termination.
To Reproduce
- Using Target Framework
netcoreapp3.1
- Using
Microsoft.Extensions.Hosting
version 3.1.2 - Run this code:
public class SyncService : IHostedService, IDisposable
{
private static object _workerMonitor = new object();
private Timer _timer;
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(Callback, null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Stopping");
_timer?.Change(0, 0);
Monitor.Enter(_workerMonitor);
Console.WriteLine("Stopped");
return Task.CompletedTask;
}
public void Dispose()
{
Console.WriteLine("Disposing");
_timer?.Dispose();
Console.WriteLine("Disposed");
}
private void Callback(object state)
{
if (Monitor.TryEnter(_workerMonitor))
try
{
Console.WriteLine("Cycle Start");
Thread.Sleep(10000);
Console.WriteLine("Cycle End");
}
catch (Exception e)
{
Console.WriteLine("Fatal Exception" + e);
}
finally
{
Monitor.Exit(_workerMonitor);
Console.WriteLine("Exited");
}
}
}
class Program
{
static void Main(string[] args)
{
try
{
var builder = new HostBuilder()
.ConfigureServices(collection => collection.AddHostedService<SyncService>())
.Build();
builder.Run();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
- Terminate the program using
CTRL+C
. This triggers theStopAsync
method. - See error
Expected behavior
The program should terminate without error message.
Actual behavior
The program terminates sometimes with error message sometimes without.
I noticed that the exception shows up if I hit CTRL+C
immediately after the “Cycle Start” message. If I wait for about 6/7 seconds the exception isn’t thrown.
Console Output if error shows up:
Cycle Start
Stopping
Cycle End
Exited
Stopped
Disposing
Disposed
System.OperationCanceledException: The operation was canceled.
at System.Threading.CancellationToken.ThrowOperationCanceledException()
at Microsoft.Extensions.Hosting.Internal.Host.StopAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.WaitForShutdownAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at ConsoleApp1.Program.Main(String[] args) in C:\Users\...\ConsoleApp1\ConsoleApp1\Program.cs:line 67
Console output without error (expected):
Cycle Start
Stopping
Cycle End
Exited
Stopped
Disposing
Disposed
Additional context
Output of dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.1.101
Commit: b377529961
Runtime Environment: OS Name: Windows OS Version: 10.0.17763 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\3.1.101\
Host (useful for support): Version: 3.1.1 Commit: a1388f194c
.NET Core SDKs installed: 2.2.402 [C:\Program Files\dotnet\sdk] 3.1.101 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed: Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:5
Top GitHub Comments
It looks like we don’t have an extension method for it on
IHostBuilder
. Might just be something we missed when moving from WebHost to GenericHost, or it might have been an intentional omission. Feel free to file a new bug asking for the extension method to be supported onIHostBuilder
! If there’s a reason we omitted it, we’ll let you know in the issue, but there’s no harm in suggesting it!This solved my actual problem because I’m using a
WebHost
.But my example uses a
HostBuilder
. ThereforeUseShutdownTimeout
is not available as a extension method. So I did some digging in the framework and found that theUseShutdownTimeout
is eventually just a configuration with the keywordshutdownTimeoutSeconds
Link to code: https://github.com/dotnet/aspnetcore/blob/8cba1cbb231b37d56c352fad196619b11c5c6613/src/Hosting/Abstractions/src/HostingAbstractionsWebHostBuilderExtensions.cs#L179-L182
Then I refactored my create
HostBuilder
code:But this ain’t working… Just out of curiosity (and because I think somebody else might stumble over the same issue) how could I solve this when I have a
HostBuilder
?