From 0a20b36c8ed106a38fc2152bb8e572dc8e93957e Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 27 May 2009 18:32:45 +0000 Subject: [PATCH] Out with ShutdownManager, in with LifecycleManager which handles both initialization and shutdown. This will obviate the need for a lot of manual wiring up. ShutdownManager still works and passes through to LifecycleManager, but switching to the new deal is encouraged. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5802 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../crowd/chat/server/ChatChannelManager.java | 13 +- .../crowd/peer/server/CrowdPeerManager.java | 6 +- .../crowd/server/PlaceRegistry.java | 10 +- .../presents/peer/server/PeerManager.java | 12 +- .../server/AbstractSignalHandler.java | 6 +- .../presents/server/ClientManager.java | 69 +++--- .../presents/server/LifecycleManager.java | 199 ++++++++++++++++++ .../presents/server/PresentsInvoker.java | 10 +- .../presents/server/PresentsServer.java | 15 +- .../presents/server/RebootManager.java | 22 +- .../presents/server/ShutdownManager.java | 81 ++----- .../server/net/ConnectionManager.java | 12 +- .../com/threerings/util/DependencyGraph.java | 31 ++- 13 files changed, 326 insertions(+), 160 deletions(-) create mode 100644 src/java/com/threerings/presents/server/LifecycleManager.java diff --git a/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java b/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java index 27d9cd1b8..914f8ee63 100644 --- a/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java +++ b/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java @@ -43,8 +43,8 @@ import com.threerings.presents.peer.data.ClientInfo; import com.threerings.presents.peer.data.NodeObject; import com.threerings.presents.peer.server.PeerManager; import com.threerings.presents.server.InvocationManager; +import com.threerings.presents.server.LifecycleManager; import com.threerings.presents.server.PresentsDObjectMgr; -import com.threerings.presents.server.ShutdownManager; import com.threerings.crowd.chat.data.ChatChannel; import com.threerings.crowd.chat.data.ChatCodes; @@ -62,7 +62,7 @@ import static com.threerings.crowd.Log.log; */ @Singleton public abstract class ChatChannelManager - implements ChannelSpeakProvider, ShutdownManager.Shutdowner + implements ChannelSpeakProvider, LifecycleManager.ShutdownComponent { /** * When a body becomes a member of a channel, this method should be called so that any server @@ -114,7 +114,7 @@ public abstract class ChatChannelManager }); } - // from interface ShutdownManager.Shutdowner + // from interface LifecycleManager.Shutdowner public void shutdown () { // stop our channel closer; always be closing... except now @@ -126,15 +126,14 @@ public abstract class ChatChannelManager * Creates our singleton manager and registers our invocation service. */ @Inject protected ChatChannelManager (PresentsDObjectMgr omgr, InvocationManager invmgr, - ShutdownManager shutmgr) + LifecycleManager lifemgr) { invmgr.registerDispatcher(new ChannelSpeakDispatcher(this), CrowdCodes.CROWD_GROUP); - shutmgr.registerShutdowner(this); + lifemgr.addComponent(this); // create and start our idle channel closer (always be closing) _closer = new Interval(omgr) { - @Override - public void expired () { + @Override public void expired () { closeIdleChannels(); } }; diff --git a/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java b/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java index f34d727bc..a854ff102 100644 --- a/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java +++ b/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java @@ -32,8 +32,8 @@ import com.threerings.presents.peer.server.PeerManager; import com.threerings.presents.peer.server.PeerNode; import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; +import com.threerings.presents.server.LifecycleManager; import com.threerings.presents.server.PresentsSession; -import com.threerings.presents.server.ShutdownManager; import com.threerings.crowd.chat.client.ChatService; import com.threerings.crowd.chat.data.UserMessage; @@ -51,9 +51,9 @@ public abstract class CrowdPeerManager extends PeerManager /** * Creates an uninitialized peer manager. */ - @Inject public CrowdPeerManager (ShutdownManager shutmgr) + @Inject public CrowdPeerManager (LifecycleManager lifeMgr) { - super(shutmgr); + super(lifeMgr); } // from interface CrowdPeerProvider diff --git a/src/java/com/threerings/crowd/server/PlaceRegistry.java b/src/java/com/threerings/crowd/server/PlaceRegistry.java index 2fdad325b..3d559645d 100644 --- a/src/java/com/threerings/crowd/server/PlaceRegistry.java +++ b/src/java/com/threerings/crowd/server/PlaceRegistry.java @@ -34,7 +34,7 @@ import com.samskivert.util.IntMaps; import com.threerings.presents.dobj.RootDObjectManager; import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; -import com.threerings.presents.server.ShutdownManager; +import com.threerings.presents.server.LifecycleManager; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; @@ -48,7 +48,7 @@ import static com.threerings.crowd.Log.log; */ @Singleton public class PlaceRegistry - implements ShutdownManager.Shutdowner + implements LifecycleManager.ShutdownComponent { /** Used in conjunction with {@link PlaceRegistry#createPlace(PlaceConfig,PreStartupHook)}. */ public static interface PreStartupHook @@ -60,9 +60,9 @@ public class PlaceRegistry * Creates and initializes the place registry. This is called by the server during its * initialization phase. */ - @Inject public PlaceRegistry (ShutdownManager shutmgr) + @Inject public PlaceRegistry (LifecycleManager lifemgr) { - shutmgr.registerShutdowner(this); + lifemgr.addComponent(this); } /** @@ -155,7 +155,7 @@ public class PlaceRegistry return _pmgrs.values().iterator(); } - // from interface ShutdownManager.Shutdowner + // from interface LifecycleManager.ShutdownComponent public void shutdown () { // shut down all active places diff --git a/src/java/com/threerings/presents/peer/server/PeerManager.java b/src/java/com/threerings/presents/peer/server/PeerManager.java index 15fedf3d4..535059723 100644 --- a/src/java/com/threerings/presents/peer/server/PeerManager.java +++ b/src/java/com/threerings/presents/peer/server/PeerManager.java @@ -65,10 +65,10 @@ import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.ClientManager; import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; -import com.threerings.presents.server.PresentsSession; +import com.threerings.presents.server.LifecycleManager; import com.threerings.presents.server.PresentsDObjectMgr; +import com.threerings.presents.server.PresentsSession; import com.threerings.presents.server.ReportManager; -import com.threerings.presents.server.ShutdownManager; import com.threerings.presents.server.net.ConnectionManager; import com.threerings.presents.peer.client.PeerService; @@ -86,7 +86,7 @@ import static com.threerings.presents.Log.log; * servers and uses those objects to communicate cross-node information. */ public abstract class PeerManager - implements PeerProvider, ClientManager.ClientObserver, ShutdownManager.Shutdowner + implements PeerProvider, ClientManager.ClientObserver, LifecycleManager.ShutdownComponent { /** * Used by entities that wish to know when cached data has become stale due to a change on @@ -198,9 +198,9 @@ public abstract class PeerManager /** * Creates an uninitialized peer manager. */ - @Inject public PeerManager (ShutdownManager shutmgr) + @Inject public PeerManager (LifecycleManager lifeMgr) { - shutmgr.registerShutdowner(this); + lifeMgr.addComponent(this); } /** @@ -753,7 +753,7 @@ public abstract class PeerManager return _stats.clone(); } - // from interface ShutdownManager.Shutdowner + // from interface LifecycleManager.ShutdownComponent public void shutdown () { if (_nodeName == null) { // sanity check diff --git a/src/java/com/threerings/presents/server/AbstractSignalHandler.java b/src/java/com/threerings/presents/server/AbstractSignalHandler.java index 742cc471f..843353500 100644 --- a/src/java/com/threerings/presents/server/AbstractSignalHandler.java +++ b/src/java/com/threerings/presents/server/AbstractSignalHandler.java @@ -52,7 +52,7 @@ public abstract class AbstractSignalHandler protected void termReceived () { log.info("Shutdown initiated by TERM signal."); - _shutmgr.queueShutdown(); + _lifemgr.queueShutdown(); } /** @@ -61,7 +61,7 @@ public abstract class AbstractSignalHandler protected void intReceived () { log.info("Shutdown initiated by INT signal."); - _shutmgr.queueShutdown(); + _lifemgr.queueShutdown(); } /** @@ -72,6 +72,6 @@ public abstract class AbstractSignalHandler log.info(_repmgr.generateReport(ReportManager.DEFAULT_TYPE)); } - @Inject protected ShutdownManager _shutmgr; + @Inject protected LifecycleManager _lifemgr; @Inject protected ReportManager _repmgr; } diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java index e1e4bd9a1..aabcf71fd 100644 --- a/src/java/com/threerings/presents/server/ClientManager.java +++ b/src/java/com/threerings/presents/server/ClientManager.java @@ -62,7 +62,7 @@ import static com.threerings.presents.Log.log; */ @Singleton public class ClientManager - implements ClientResolutionListener, ReportManager.Reporter, ShutdownManager.Shutdowner + implements ClientResolutionListener, ReportManager.Reporter, LifecycleManager.Component { /** * Used by {@link ClientManager#applyToClient}. @@ -100,21 +100,10 @@ public class ClientManager /** * Constructs a client manager that will interact with the supplied connection manager. */ - @Inject public ClientManager ( - ReportManager repmgr, ShutdownManager shutmgr, PresentsDObjectMgr omgr) + @Inject public ClientManager (ReportManager repmgr, LifecycleManager lifemgr) { repmgr.registerReporter(this); - shutmgr.registerShutdowner(this); - - // start up an interval that will check for expired clients and flush them from the bowels - // of the server - _flushClients = new Interval(omgr) { - @Override - public void expired () { - flushClients(); - } - }; - _flushClients.schedule(CLIENT_FLUSH_INTERVAL, true); + lifemgr.addComponent(this); } /** @@ -126,26 +115,6 @@ public class ClientManager _injector = injector; } - // from interface ShutdownManager.Shutdowner - public void shutdown () - { - log.info("Client manager shutting down", "ccount", _usermap.size()); - - _flushClients.cancel(); - - // inform all of our clients that they are being shut down - synchronized (_usermap) { - for (PresentsSession pc : _usermap.values()) { - try { - pc.shutdown(); - } catch (Exception e) { - log.warning("Client choked in shutdown()", - "client", StringUtil.safeToString(pc), e); - } - } - } - } - /** * Configures the client manager with a factory for creating {@link PresentsSession} and {@link * ClientResolver} classes for authenticated client connections. @@ -354,6 +323,38 @@ public class ClientManager _omgr.destroyObject(clobj.getOid()); } + // from interface LifecycleManager.Component + public void init () + { + // start up an interval that will check for and flush expired clients + _flushClients = new Interval(_omgr) { + @Override public void expired () { + flushClients(); + } + }; + _flushClients.schedule(CLIENT_FLUSH_INTERVAL, true); + } + + // from interface LifecycleManager.Component + public void shutdown () + { + log.info("Client manager shutting down", "ccount", _usermap.size()); + + _flushClients.cancel(); + + // inform all of our clients that they are being shut down + synchronized (_usermap) { + for (PresentsSession pc : _usermap.values()) { + try { + pc.shutdown(); + } catch (Exception e) { + log.warning("Client choked in shutdown()", + "client", StringUtil.safeToString(pc), e); + } + } + } + } + /** * Renames a currently connected client from oldname to newname. * diff --git a/src/java/com/threerings/presents/server/LifecycleManager.java b/src/java/com/threerings/presents/server/LifecycleManager.java new file mode 100644 index 000000000..aebea38ac --- /dev/null +++ b/src/java/com/threerings/presents/server/LifecycleManager.java @@ -0,0 +1,199 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2008 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/narya/ +// +// This library 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 library 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 library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.server; + +import com.google.inject.Inject; + +import com.samskivert.util.ObserverList; +import com.samskivert.util.RunQueue; +import com.threerings.util.DependencyGraph; + +import com.threerings.presents.annotation.EventQueue; + +import static com.threerings.presents.Log.log; + +/** + * Manages the lifecycle (initialization and shutdown) of our various server managers and other + * entities. + */ +public class LifecycleManager +{ + /** An interface implemented by components which wish to participate in the lifecycle. */ + public interface BaseComponent + { + } + + /** An interface implemented by components which wish to participate in the lifecycle. */ + public interface InitComponent extends BaseComponent + { + /** Called after dependencies have been fully resolved to initialize this component. */ + public void init (); + + /** Called when the server is shutting down. */ + public void shutdown (); + } + + /** An interface implemented by components which wish to participate in the lifecycle. */ + public interface ShutdownComponent extends BaseComponent + { + /** Called when the server is shutting down. */ + public void shutdown (); + } + + /** An interface implemented by components which wish to participate in the lifecycle. */ + public interface Component extends InitComponent, ShutdownComponent + { + } + + /** Constraints for use with {@link #addConstraint}. */ + public static enum Constraint { RUNS_BEFORE, RUNS_AFTER }; + + /** + * Registers a component with the manager. This should be done during the dependency resolution + * phase by injecting the LifecycleManager into your constructor and calling this method there. + */ + public void addComponent (BaseComponent comp) + { + if (_initers == null || _downers == null) { + throw new IllegalStateException("Too late to register component."); + } + if (comp instanceof InitComponent) { + _initers.add((InitComponent)comp); + } + if (comp instanceof ShutdownComponent) { + _downers.add((ShutdownComponent)comp); + } + } + + /** + * Removes a component from the manager. This is generally not used. + */ + public void removeComponent (BaseComponent comp) + { + if (_initers != null && comp instanceof InitComponent) { + _initers.remove((InitComponent)comp); + } + if (_downers != null && comp instanceof ShutdownComponent) { + _downers.remove((ShutdownComponent)comp); + } + } + + /** + * Adds a constraint that a certain component must be initialized before another. + */ + public void addInitConstraint (InitComponent lhs, Constraint constraint, InitComponent rhs) + { + if (lhs == null || rhs == null) { + throw new IllegalArgumentException("Cannot add constraint about null component."); + } + InitComponent before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs; + InitComponent after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs; + _initers.addDependency(after, before); + } + + /** + * Adds a constraint that a certain component must be shutdown before another. + */ + public void addShutdownConstraint (ShutdownComponent lhs, Constraint constraint, + ShutdownComponent rhs) + { + if (lhs == null || rhs == null) { + throw new IllegalArgumentException("Cannot add constraint about null component."); + } + ShutdownComponent before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs; + ShutdownComponent after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs; + _downers.addDependency(after, before); + } + + /** + * Queues up a request to shutdown on the event thread. This method may be safely called from + * any thread. + */ + public void queueShutdown () + { + _eventQueue.postRunnable(new Runnable() { + public void run () { + shutdown(); + } + }); + } + + /** + * Returns true if we're in the process of shutting down. + */ + public boolean isShuttingDown () + { + return (_downers == null); + } + + /** + * Initializes all components immediately on the caller's thread. + */ + public void init () + { + if (_initers == null) { + log.warning("Refusing repeat init() request."); + return; + } + + ObserverList list = _initers.toObserverList(); + _initers = null; + list.apply(new ObserverList.ObserverOp() { + public boolean apply (InitComponent comp) { + log.debug("Initializing component", "comp", comp); + comp.init(); + return true; + } + }); + } + + /** + * Shuts down all components immediately on the caller's thread. + */ + public void shutdown () + { + if (_downers == null) { + log.warning("Refusing repeat shutdown() request."); + return; + } + + ObserverList list = _downers.toObserverList(); + _downers = null; + list.apply(new ObserverList.ObserverOp() { + public boolean apply (ShutdownComponent comp) { + log.debug("Shutting down component", "comp", comp); + comp.shutdown(); + return true; + } + }); + } + + /** The queue we'll use to get onto the event thread before shutting down. */ + @Inject @EventQueue protected RunQueue _eventQueue; + + /** A dependency graph of our components arranged by initialization dependencies. */ + protected DependencyGraph _initers = new DependencyGraph(); + + /** A dependency graph of our components arranged by shutdown dependencies. */ + protected DependencyGraph _downers = + new DependencyGraph(); +} diff --git a/src/java/com/threerings/presents/server/PresentsInvoker.java b/src/java/com/threerings/presents/server/PresentsInvoker.java index 0c59d695d..7f8a6b923 100644 --- a/src/java/com/threerings/presents/server/PresentsInvoker.java +++ b/src/java/com/threerings/presents/server/PresentsInvoker.java @@ -33,17 +33,17 @@ import static com.threerings.presents.Log.log; */ @Singleton public class PresentsInvoker extends ReportingInvoker - implements ShutdownManager.Shutdowner + implements LifecycleManager.ShutdownComponent { - @Inject public PresentsInvoker ( - PresentsDObjectMgr omgr, ShutdownManager shutmgr, ReportManager repmgr) + @Inject public PresentsInvoker (PresentsDObjectMgr omgr, LifecycleManager lifemgr, + ReportManager repmgr) { super("presents.Invoker", omgr, repmgr); _omgr = omgr; - shutmgr.registerShutdowner(this); + lifemgr.addComponent(this); } - @Override // from Invoker + @Override // from Invoker, LifecycleManager.ShutdownComponent public void shutdown () { // this will do a sophisticated shutdown of both ourself and the dobjmgr; note: we diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java index a49df79f5..9a2d1437b 100644 --- a/src/java/com/threerings/presents/server/PresentsServer.java +++ b/src/java/com/threerings/presents/server/PresentsServer.java @@ -159,10 +159,13 @@ public class PresentsServer // initialize the time base services TimeBaseProvider.init(_invmgr, _omgr); - // Make the client manager shut down before the invoker and dobj threads. This will help - // application code to avoid long chains of shutdown constraints (e.g. msoy bureau manager). - _shutmgr.addConstraint( - _clmgr, ShutdownManager.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker); + // make the client manager shut down before the invoker and dobj threads; this helps + // application code to avoid long chains of shutdown constraints + _lifemgr.addShutdownConstraint( + _clmgr, LifecycleManager.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker); + + // initialize all of our registered components + _lifemgr.init(); } /** @@ -238,8 +241,8 @@ public class PresentsServer /** The manager of invocation services. */ @Inject protected InvocationManager _invmgr; - /** Handles orderly shutdown of our managers, etc. */ - @Inject protected ShutdownManager _shutmgr; + /** Handles orderly initialization and shutdown of our managers, etc. */ + @Inject protected LifecycleManager _lifemgr; /** Handles generation of state of the server reports. */ @Inject protected ReportManager _repmgr; diff --git a/src/java/com/threerings/presents/server/RebootManager.java b/src/java/com/threerings/presents/server/RebootManager.java index 4ed7582b1..37b488b4b 100644 --- a/src/java/com/threerings/presents/server/RebootManager.java +++ b/src/java/com/threerings/presents/server/RebootManager.java @@ -155,8 +155,7 @@ public abstract class RebootManager _rebootSoon = false; long firstWarnTime = (_nextReboot - (WARNINGS[0] * 60 * 1000)) - now; _interval = new Interval(_omgr) { - @Override - public void expired () { + @Override public void expired () { doWarning(0); } }; @@ -189,9 +188,9 @@ public abstract class RebootManager /** * Provides us with our dependencies. */ - protected RebootManager (ShutdownManager shutmgr, RootDObjectManager omgr) + protected RebootManager (LifecycleManager lmgr, RootDObjectManager omgr) { - _shutmgr = shutmgr; + _lmgr = lmgr; _omgr = omgr; } @@ -257,12 +256,11 @@ public abstract class RebootManager // wait 1 second, then do it new Interval() { // Note: This interval does not run on the dobj thread - @Override - public void expired () { + @Override public void expired () { // ...but we then post a LongRunnable... _omgr.postRunnable(new PresentsDObjectMgr.LongRunnable() { public void run () { - _shutmgr.shutdown(); + _lmgr.shutdown(); } }); } @@ -279,8 +277,7 @@ public abstract class RebootManager // schedule the next warning _interval = new Interval(_omgr) { - @Override - public void expired () { + @Override public void expired () { doWarning(level + 1); } }; @@ -301,8 +298,7 @@ public abstract class RebootManager log.info("Reboot delayed due to outstanding locks", "locks", _rebootLocks.elements()); broadcast("m.reboot_delayed"); _interval = new Interval(_omgr) { - @Override - public void expired () { + @Override public void expired () { doWarning(WARNINGS.length); } }; @@ -325,8 +321,8 @@ public abstract class RebootManager }); } - /** Our shutdown manager, which is how we will reboot. */ - protected ShutdownManager _shutmgr; + /** Our lifecycle manager, which is how we will reboot. */ + protected LifecycleManager _lmgr; /** Our distributed object manager. */ protected RootDObjectManager _omgr; diff --git a/src/java/com/threerings/presents/server/ShutdownManager.java b/src/java/com/threerings/presents/server/ShutdownManager.java index bfaed9a1a..a418e4607 100644 --- a/src/java/com/threerings/presents/server/ShutdownManager.java +++ b/src/java/com/threerings/presents/server/ShutdownManager.java @@ -24,44 +24,28 @@ package com.threerings.presents.server; import com.google.inject.Inject; import com.google.inject.Singleton; -import com.samskivert.util.ObserverList; -import com.samskivert.util.RunQueue; - -import com.threerings.util.DependencyGraph; - -import com.threerings.presents.annotation.EventQueue; - -import static com.threerings.presents.Log.log; - /** * Handles the orderly shutdown of all server services. + * + * @Deprecated use LifecycleManager */ @Singleton public class ShutdownManager { /** Implementers of this interface will be notified when the server is shutting down. */ - public static interface Shutdowner + public static interface Shutdowner extends LifecycleManager.ShutdownComponent { - /** - * Called when the server is shutting down. - */ - void shutdown (); } /** Constraints for use with {@link ShutdownManager#addConstraint}. */ public static enum Constraint { RUNS_BEFORE, RUNS_AFTER }; - @Inject ShutdownManager (@EventQueue RunQueue dobjq) - { - _dobjq = dobjq; - } - /** * Registers an entity that will be notified when the server is shutting down. */ public void registerShutdowner (Shutdowner downer) { - _downers.add(downer); + _lifeMgr.addComponent(downer); } /** @@ -69,7 +53,7 @@ public class ShutdownManager */ public void unregisterShutdowner (Shutdowner downer) { - _downers.remove(downer); + _lifeMgr.removeComponent(downer); } /** @@ -77,12 +61,14 @@ public class ShutdownManager */ public void addConstraint (Shutdowner lhs, Constraint constraint, Shutdowner rhs) { - if (lhs == null || rhs == null) { - throw new IllegalArgumentException("Cannot add constraint about null shutdowner."); + switch (constraint) { + case RUNS_BEFORE: + _lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_BEFORE, rhs); + break; + case RUNS_AFTER: + _lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_AFTER, rhs); + break; } - Shutdowner before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs; - Shutdowner after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs; - _downers.addDependency(after, before); } /** @@ -91,11 +77,7 @@ public class ShutdownManager */ public void queueShutdown () { - _dobjq.postRunnable(new Runnable() { - public void run () { - shutdown(); - } - }); + _lifeMgr.queueShutdown(); } /** @@ -103,41 +85,8 @@ public class ShutdownManager */ public boolean isShuttingDown () { - return _downers == null; + return _lifeMgr.isShuttingDown(); } - /** - * Shuts down all shutdowners immediately on the caller's thread. - */ - public void shutdown () - { - if (_downers == null) { - log.warning("Refusing repeat shutdown request."); - return; - } - - ObserverList downers = ObserverList.newSafeInOrder(); - - while (!_downers.isEmpty()) { - downers.add(_downers.removeAvailableElement()); - } - - _downers = null; - - // shut down all shutdown participants - downers.apply(new ObserverList.ObserverOp() { - public boolean apply (Shutdowner downer) { - log.debug("Calling shutdown registrant", "downer", downer); - downer.shutdown(); - return true; - } - }); - } - - /** All of the registered shutdowners along with related constraints. */ - protected DependencyGraph _downers = new DependencyGraph(); - - /** The queue we'll use to get onto the dobjmgr thread before shutting down. */ - protected RunQueue _dobjq; - + @Inject protected LifecycleManager _lifeMgr; } diff --git a/src/java/com/threerings/presents/server/net/ConnectionManager.java b/src/java/com/threerings/presents/server/net/ConnectionManager.java index 5119da538..15b681c05 100644 --- a/src/java/com/threerings/presents/server/net/ConnectionManager.java +++ b/src/java/com/threerings/presents/server/net/ConnectionManager.java @@ -67,9 +67,9 @@ import com.threerings.presents.server.Authenticator; import com.threerings.presents.server.ChainedAuthenticator; import com.threerings.presents.server.ClientManager; import com.threerings.presents.server.DummyAuthenticator; +import com.threerings.presents.server.LifecycleManager; import com.threerings.presents.server.PresentsDObjectMgr; import com.threerings.presents.server.ReportManager; -import com.threerings.presents.server.ShutdownManager; import com.threerings.presents.util.DatagramSequencer; import static com.threerings.presents.Log.log; @@ -82,15 +82,15 @@ import static com.threerings.presents.Log.log; */ @Singleton public class ConnectionManager extends LoopingThread - implements ShutdownManager.Shutdowner, ReportManager.Reporter + implements LifecycleManager.ShutdownComponent, ReportManager.Reporter { /** * Creates a connection manager instance. Don't call this, Guice will do it for you. */ - @Inject public ConnectionManager (ShutdownManager shutmgr, ReportManager repmgr) + @Inject public ConnectionManager (LifecycleManager lifemgr, ReportManager repmgr) { super("ConnectionManager"); - shutmgr.registerShutdowner(this); + lifemgr.addComponent(this); repmgr.registerReporter(this); } @@ -391,7 +391,7 @@ public class ConnectionManager extends LoopingThread // if we failed to listen on at least one port, give up the ghost if (successes == 0) { log.warning("ConnectionManager failed to bind to any ports. Shutting down."); - _shutmgr.queueShutdown(); + _lifemgr.queueShutdown(); return; } @@ -1232,7 +1232,7 @@ public class ConnectionManager extends LoopingThread @Inject @AuthInvoker protected Invoker _authInvoker; @Inject protected PresentsDObjectMgr _omgr; @Inject protected ClientManager _clmgr; - @Inject protected ShutdownManager _shutmgr; + @Inject protected LifecycleManager _lifemgr; /** How long we wait for network events before checking our running flag to see if we should * still be running. We don't want to loop too tightly, but we need to make sure we don't sit diff --git a/src/java/com/threerings/util/DependencyGraph.java b/src/java/com/threerings/util/DependencyGraph.java index cbf272938..be075731b 100644 --- a/src/java/com/threerings/util/DependencyGraph.java +++ b/src/java/com/threerings/util/DependencyGraph.java @@ -1,3 +1,6 @@ +// +// $Id$ + package com.threerings.util; import java.util.ArrayList; @@ -6,12 +9,15 @@ import java.util.HashMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.samskivert.util.ObserverList; + /** - * Maintains a bidirectional graph to manage the order that the items are removed. Children - * must wait until their parents are accessed - thus removing an available element means that - * a node without parents (an orphan) is removed and returned and the rest of the graph is - * updated to reflect that removal. - * @param + * Maintains a bidirectional graph to manage the order that the items are removed. Children must + * wait until their parents are accessed - thus removing an available element means that a node + * without parents (an orphan) is removed and returned and the rest of the graph is updated to + * reflect that removal. + * + * @param the type of object maintained in the graph. */ public class DependencyGraph { @@ -49,7 +55,7 @@ public class DependencyGraph /** * Removes and returns an element which is available, meaning not dependent upon any other - * still in the graph. + * still in the graph. */ public T removeAvailableElement () { @@ -126,6 +132,19 @@ public class DependencyGraph return false; } + /** + * Flattens this graph into an observer list in dependencys order. Empties the graph in the + * process. + */ + public ObserverList toObserverList () + { + ObserverList list = ObserverList.newSafeInOrder(); + while (!isEmpty()) { + list.add(removeAvailableElement()); + } + return list; + } + /** All the nodes included in the graph. */ protected HashMap> _nodes = Maps.newHashMap();