When run via the Service Control Manager do Windows services need to assume that command processing methods (OnStart, OnStop, etc.) can be called on different threads with nothing ensuring that, e.g., assignments to members will be visible between methods?
public class MyService : ServiceBase {
private Object _foo;
protected override void OnStart(string[] args) {
_foo = new Object();
}
protected override void OnStop() {
if (_foo == null) {
throw new Exception("Assignment not visible"); // Can this happen?
}
_foo = null;
}
}
I can't find a guarantee that the exception in my example won't be thrown, but all of the examples I've found, including elsewhere on StackOverflow, seems to assume that, e.g., assignments to variables in OnStart() will always be visible in OnStop().
If no such guarantee is made by the SCM I do know how to ensure the assignment is visible (e.g. by adding a lock around all reads/writes in the service). I'm interested in whether or not such measures are necessary.
In one sense, the SCM cannot guarantee that the exception you outline will not be thrown. It does not control the service's manipulation of a private member of course - e.g. if additional service code affects
_foo
.Having said this, consider the following scenario to see why the answer to your specific question is clearly no:
1) Build your service with the following changes to demonstrate:
2) Open a command shell.
3) Open another command shell.
4) In the first command shell, install the service (with
sc create
) if you haven't already, and start it (withnet start
). You should see:The trailing dots should be added one by one as the SCM waits through the 10 seconds of sleeping to start the service.
5) In the second command shell, try to stop the service (with
net stop
) before 10 seconds elapse. You should see:So starting a service is clearly a blocking operation that must complete before the service can be stopped.
6) Check the first command shell after 10 seconds have elapsed. You should see:
7) Return to the second command shell and try to stop the service again. You should see:
8) Review the resulting log - for example:
Starting and stopping the service quickly is easier with two command shells I think; but the example works similarly with one command shell too.
Lastly, you might find Mitchell Taylor (CoolDadTx)'s answer to a similar question in MSDN forums interesting as I did:
UPDATE:
Particularly note the Service State Transitions MSDN article to which the article that Mitchell Taylor cites links. It contains a state diagram that pretty clearly & authoritatively documents defined service state transitions and aligns with what I outlined above. It also explains in relation to the state diagram how the SCM does not transmit service control requests at times to ensure only defined state transitions.