In https://community.jboss.org/thread/201784 I raised an issue in HornetQServerImpl, where its start() method may spawn new threads to initialize some components, and those components may further spawn more threads to do part of their own initialization, in some cases using a thread pool, and so on. The issue happens when start() finished, some component have not been started yet because it delegates to a separate thread. If stop() is called immediately and the not-started component gets stopped before it gets started, the stop() will have no effect and the component will be started in the end, as if no 'stop' has ever called on it. The result is that the component stay alive after the stop() call. On the other hand, if stop() of the server/component has some other thread/pool to do part of its job, it will cause similar issue when start() is called right after, resulting in some component is stopped when the server is started.
To avoid the problem, theoretically we can wait for all related thread/pool to stop before doing any start() and stop() call. But for a complicated system like HornetQServerImpl, which contains a lot of other components and intense use of shared thread pool, doing that may not be practical. Take a look at its start() and stop() and you will get an idea.
Here I propose a simple, generic way to guarantee clean start/stop of a component. I attached all the source code here.
Class StartStopGuard can be used to synchronize between any start() and stop() for a component. It keeps a ON/OFF switch object and a counter. It is so implemented that to change its switch from ON to OFF, or vice versa, the counter must be zero. Each spawned thread in start() or stop() can be given a handle (StartStopHandle) to control when it is ready to release it. Whenever a handle is created, the counter increases by one, whenever a handle is released the counter decreases by one.
When a component is started, it first call StartStopGuard.start(0) to set the guard to ON and get a handle. If there are some threads that needs to synchronize with the startup process, it will create a new handle and passed to the thread (a ControlledRunnable class is provided to make the passing of the handle easy via a ThreadLocal variable). Whenever such a thread (actually it is the component running on the thread) feels it is safe for a stop to happen, it can release the handle. (like the end of the method, or right before it starts to wait on some signal).
When it is stopped, it first to call StartStopGuard.stop(0) to change the switch to OFF. If at the moment the guard's count is not zero, which means some threads hold the handles for protection, the guard will wait for the count to be zero before change the switch and return. That'll make sure the stop process won't mess up with the start().
To illustrate the use of the StartStopGuard, take a look at TestComponentWithGuard and TestComponentWithoutGuard and run them to see the different result.
In summary this guard can guarantee safe start()/stop(). It can also prevents a component from starting twice, or stopping twice.
Any comments are welcomed.
start_stop_guard.zip 4.8 KB