-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MSC-193] Fixing broken ServiceContainer shutdown process.
There were two kind of problems with previous solution: [1] shutdownListener was executed in regular listeners chain [2] race condition between shutdown() and install() methods Both aforementioned problems sometimes caused unexpected premature executor shutdown while there were still pending tasks (either scheduled or not) to be executed. This caused many broken scenarios to happen. One example of broken scenario (assuming [1] was the problem): 1) There is only one service A in registry in UP state. It has 1 registered transition listener. 2) Thread {1} called Container.shutdown() and this method completed successfully. It associated shutdownListener with controller for A and scheduled it for removal. There are two listeners now in controller for A and A is transitioning to REMOVED state. 3) A is not in REMOVED state yet 4) User code executing in Thread {2} calls Container.awaitStability(). This method hangs because previous Container.shutdown() asked service A to stop, but A is still transitioning. 5) A is entering REMOVED state and its RemoveTask is finished. 6) Last step is to notify listeners that REMOVING -> REMOVED transition happened. 7) shutdownListener is executed first and because it was registered just with one service it calls executor.shutdown(). Executor is terminated. 8) Problem is there is pending last listener task not executed yet and because it didn't execute yet stability notification was not sent to service container that controller stability was achieved. User code in Thread {2} will hang forever. Similar example of broken scenario can be constructed assuming [2] was the initial problem. This fix: * removes terminateListener from regular controller listeners chain and creates separate and last controller phase intended just for container termination listeners. When controller enters REMOVED state and its stability is achieved (i.e. all its tasks have been executed) terminate listener is being called. This is the only appropriate time to call it. * container instrinsic lock is being held while container.install() is associating controller with primary and alias registrations. Only so it can be guaranteed shutdown() will not see stale registry data (no race condition)
- Loading branch information
Showing
4 changed files
with
209 additions
and
97 deletions.
There are no files selected for viewing
64 changes: 64 additions & 0 deletions
64
src/main/java/org/jboss/msc/service/ContainerShutdownListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* JBoss, Home of Professional Open Source. | ||
* Copyright 2017, Red Hat, Inc., and individual contributors | ||
* as indicated by the @author tags. See the copyright.txt file in the | ||
* distribution for a full listing of individual contributors. | ||
* | ||
* This is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as | ||
* published by the Free Software Foundation; either version 2.1 of | ||
* the License, or (at your option) any later version. | ||
* | ||
* This software is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this software; if not, write to the Free | ||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
*/ | ||
|
||
package org.jboss.msc.service; | ||
|
||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; | ||
|
||
/** | ||
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a> | ||
*/ | ||
final class ContainerShutdownListener { | ||
@SuppressWarnings({ "UnusedDeclaration" }) | ||
private volatile int count = 1; | ||
@SuppressWarnings({ "UnusedDeclaration" }) | ||
private volatile int done; | ||
@SuppressWarnings({ "RawUseOfParameterizedType" }) | ||
private static final AtomicIntegerFieldUpdater<ContainerShutdownListener> countUpdater = AtomicIntegerFieldUpdater.newUpdater(ContainerShutdownListener.class, "count"); | ||
@SuppressWarnings({ "RawUseOfParameterizedType" }) | ||
private static final AtomicIntegerFieldUpdater<ContainerShutdownListener> doneUpdater = AtomicIntegerFieldUpdater.newUpdater(ContainerShutdownListener.class, "done"); | ||
private final Runnable callback; | ||
|
||
ContainerShutdownListener(final Runnable callback) { | ||
this.callback = callback; | ||
} | ||
|
||
final void controllerAlive() { | ||
countUpdater.getAndIncrement(this); | ||
} | ||
|
||
final void controllerDied() { | ||
tick(); | ||
} | ||
|
||
final void done() { | ||
if (doneUpdater.getAndSet(this, 1) == 0) { | ||
tick(); | ||
} | ||
} | ||
|
||
private void tick() { | ||
if (countUpdater.decrementAndGet(this) == 0) { | ||
callback.run(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.