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
This commit is contained in:
Michael Bayne
2009-05-27 18:32:45 +00:00
parent eb77929940
commit 0a20b36c8e
13 changed files with 326 additions and 160 deletions
@@ -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();
}
};
@@ -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
@@ -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
@@ -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
@@ -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;
}
@@ -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 <code>oldname</code> to <code>newname</code>.
*
@@ -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<InitComponent> list = _initers.toObserverList();
_initers = null;
list.apply(new ObserverList.ObserverOp<InitComponent>() {
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<ShutdownComponent> list = _downers.toObserverList();
_downers = null;
list.apply(new ObserverList.ObserverOp<ShutdownComponent>() {
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<InitComponent> _initers = new DependencyGraph<InitComponent>();
/** A dependency graph of our components arranged by shutdown dependencies. */
protected DependencyGraph<ShutdownComponent> _downers =
new DependencyGraph<ShutdownComponent>();
}
@@ -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
@@ -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;
@@ -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;
@@ -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<Shutdowner> downers = ObserverList.newSafeInOrder();
while (!_downers.isEmpty()) {
downers.add(_downers.removeAvailableElement());
}
_downers = null;
// shut down all shutdown participants
downers.apply(new ObserverList.ObserverOp<Shutdowner>() {
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<Shutdowner> _downers = new DependencyGraph<Shutdowner>();
/** The queue we'll use to get onto the dobjmgr thread before shutting down. */
protected RunQueue _dobjq;
@Inject protected LifecycleManager _lifeMgr;
}
@@ -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
@@ -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 <T>
* 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 <T> the type of object maintained in the graph.
*/
public class DependencyGraph<T>
{
@@ -49,7 +55,7 @@ public class DependencyGraph<T>
/**
* 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<T>
return false;
}
/**
* Flattens this graph into an observer list in dependencys order. Empties the graph in the
* process.
*/
public ObserverList<T> toObserverList ()
{
ObserverList<T> list = ObserverList.newSafeInOrder();
while (!isEmpty()) {
list.add(removeAvailableElement());
}
return list;
}
/** All the nodes included in the graph. */
protected HashMap<T, DependencyNode<T>> _nodes = Maps.newHashMap();