I have a TopShelf service that uses async code to connect to web services and other application servers.
If it's unable to initialize its connections on startup, the service should log some errors and gracefully stop.
I've had a look at this question about stopping TopShelf when the start conditions aren't met. This answer talks about using the TopShelf HostControl to stop the service.
However, that answer relies on the ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start)
method.
I am currently configuring the TopShelf service in the standard way:
x.Service<MyService>(s =>
{
s.ConstructUsing(() => new MyService());
s.WhenStarted(s => s.Start());
s.WhenStopped(s => s.Stop());
});
However my service's Start()
method is actually async
, defined like this:
public async void Start()
{
await Init();
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
This seems to work fine. But I use the await keyword in several places in the function. So, I can't simply change my Start()
method to take a HostControl
and return a bool
, because I would have to return Task<bool>
from an async
method.
I'm currently allowing exceptions to bubble up from the Start() function so that TopShelf can see them and automatically stop the service when the exception bubbles up. However, the exceptions are then totally unhandled by my code, and I therefore end up with nasty unhandled exception error messages in the various logs I write to. Which I would prefer to replace with a nice error message and a clean service shut-down.
So, I have two questions:
- Is there any problem with using an
async void Start()
method for TopShelf? - Is there a way to make it so that if
Init()
throws an exception, the exception details are gracefully logged and then the service stops, given that my service runsasync
code?
_serviceTask
returned byExecuteAsync()
actually starts, without awaiting it in theStart()
method? I can't await it there, because that would requireStart()
to be async. In test code with a mockPoll()
method based onTask.Delay()
, when I call_serviceTask.Start()
, anInvalidOperationException
is thrown with the messageStart may not be called on a promise-style task.
If I don't await or start the Task it seems to remain in theWaitingForActivation
state? – Filemon