-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make service wait on its state before stopping #84447
Conversation
Tagging subscribers to this area: @dotnet/area-extensions-hosting Issue DetailsFix: #84438 Make sure the service allows SCM to transition to Running and Stopped before exiting the test service.
|
Host was stopped before it was completely started? Just curious what would happen if it happened to a real service? (the test failure log looks weird, could not tell for sure, looks like it was not stopped) |
Oops sorry, wrong button clicked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGMT
Yes, that's more or less what I suspect.
I wouldn't expect a real service to be doing this - stopping itself immediately after starting. Probably it would always do some work before stopping. It's possible there is still some bug here in how StopAsync works -- it's possible SCM itself has a bug -- that was never tested before -- not even on ServiceBase. We should keep an eye out for how this test behaves.
The service was left running and we timed out waiting for the status to change to stopped. The remotexecutor infrastructure then gathered a dump of the process when it was disposed. You can fetch the dump and console log with
The goal of this test was to prove that |
Thank you for the broad explanation, makes sense! |
After writing this up and being annoyed with how little information I got from the dump, I tried a different debugger:
So that means we are waiting at the following states: runtime/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceLifetime.cs Line 115 in cc01464
Line 152 in cc01464
This agrees with what I shared above. We've already I can dump what state the service believes it's in:
So runtime/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs Line 140 in 02fae07
So if we look at where we set it to RUNNING, it's here: Lines 820 to 844 in cc01464
Right before that, it called runtime/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceLifetime.cs Lines 120 to 124 in de5a04b
Note at this point the main thread waiting on host.Start() is free to proceed as if the service is started, but it's not. The OnStart call was happening on a background thread and completes, after which, the main dispatcher thread waits for that background thread to signal, then sets the service to RUNNING from that ServiceMainCallback thread: Lines 920 to 933 in cc01464
If we were to stop the service in this time (between OnStop() and ServiceMainCallback completing), it's somewhat unpredictable what would happen -- since SCM is still waiting for the service's main routine to register it's handler function and return. I would place a bet that's what's happening. Essentially ServiceBase provides no way for a derived service to know once it's actually fully started. OnStart happens some time before it's actually done telling SCM that it's started. I think you could have the same problem with ServiceBase alone if you queued work in OnStart that finished super fast, then called Stop. TL;DR - waiting for service to enter RUNNING state might be the right thing to do regardless. I might see if there's any way to keep WaitForStartAsync blocked a little longer to guarantee that the service is started. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for fixing this.
…5661) * Make WindowsServiceLifetime gracefully stop (#83892) * Make WindowsServiceLifetime gracefully stop WindowsServiceLifetime was not waiting for ServiceBase to stop the service. As a result we would sometimes end the process before notifying service control manager that the service had stopped -- resulting in an error in the eventlog and sometimes a service restart. We also were permitting multiple calls to Stop to occur - through SCM callbacks, and through public API. We must not call SetServiceStatus again once the service is marked as stopped. * Alternate approach to ensuring we only ever set STATE_STOPPED once. * Avoid calling ServiceBase.Stop on stopped service I fixed double-calling STATE_STOPPED in ServiceBase, but this fix will not be present on .NETFramework. Workaround that by avoiding calling ServiceBase.Stop when the service has already been stopped by SCM. * Add tests for WindowsServiceLifetime These tests leverage RemoteExecutor to avoid creating a separate service assembly. * Respond to feedback and add more tests. This better integrates with the RemoteExecutor component as well, by hooking up the service process and fetching its handle. This gives us the correct logging and exitcode handling from RemoteExecutor. * Honor Cancellation in StopAsync * Fix bindingRedirects in RemoteExecutor * Use Async lambdas for service testing * Fix issue on Win7 where duplicate service descriptions are disallowed * Respond to feedback * Fix comment and add timeout * Fix test condition # Conflicts: # src/libraries/Microsoft.Extensions.Hosting.WindowsServices/tests/UseWindowsServiceTests.cs * Enable M.E.H.WindowsServices and S.SP.ServiceController for servicing * Make service wait on its state before stopping (#84447) * Fix WindowsService Tests where RemoteExecutor is unsupported # Conflicts: # src/libraries/Microsoft.Extensions.Hosting.WindowsServices/tests/UseWindowsServiceTests.cs * Port changes to 6.0 codebase * Version Microsoft.Windows.Compatibility --------- Co-authored-by: Vladimir Sadov <vsadov@microsoft.com>
…5656) * Make WindowsServiceLifetime gracefully stop (#83892) * Make WindowsServiceLifetime gracefully stop WindowsServiceLifetime was not waiting for ServiceBase to stop the service. As a result we would sometimes end the process before notifying service control manager that the service had stopped -- resulting in an error in the eventlog and sometimes a service restart. We also were permitting multiple calls to Stop to occur - through SCM callbacks, and through public API. We must not call SetServiceStatus again once the service is marked as stopped. * Alternate approach to ensuring we only ever set STATE_STOPPED once. * Avoid calling ServiceBase.Stop on stopped service I fixed double-calling STATE_STOPPED in ServiceBase, but this fix will not be present on .NETFramework. Workaround that by avoiding calling ServiceBase.Stop when the service has already been stopped by SCM. * Add tests for WindowsServiceLifetime These tests leverage RemoteExecutor to avoid creating a separate service assembly. * Respond to feedback and add more tests. This better integrates with the RemoteExecutor component as well, by hooking up the service process and fetching its handle. This gives us the correct logging and exitcode handling from RemoteExecutor. * Honor Cancellation in StopAsync * Fix bindingRedirects in RemoteExecutor * Use Async lambdas for service testing * Fix issue on Win7 where duplicate service descriptions are disallowed * Respond to feedback * Fix comment and add timeout * Fix test condition * Enable M.E.H.WindowsServices and S.SP.ServiceController for servicing * Make service wait on its state before stopping (#84447) * Fix WindowsService Tests where RemoteExecutor is unsupported * Enable MS.W.C for servicing * Reference latest Microsoft.Extensions.Logging.Abstractions This package has been serviced and we compile against the serviced version of its assemblies. None of the directly referenced projects have been serviced so our package doesn't restore the serviced versions. Lift up the dependency on Logging.Abstractions to ensure we reference the serviced package. --------- Co-authored-by: Vladimir Sadov <vsadov@microsoft.com>
Fix: #84438
Make sure the service allows SCM to transition to Running and Stopped before exiting the test service.