New very safe shutdown scheme. An Invoker.Unit runs on both the Invoker

and dobj thread until they're both empty (or it has been passed 50 times)
and then shuts the threads down.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2744 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2003-08-08 03:11:55 +00:00
parent 8ceaea36ed
commit b59b9b5954
3 changed files with 108 additions and 71 deletions
@@ -1,5 +1,5 @@
//
// $Id: PresentsDObjectMgr.java,v 1.34 2003/06/15 16:47:33 mdb Exp $
// $Id: PresentsDObjectMgr.java,v 1.35 2003/08/08 03:11:55 ray Exp $
package com.threerings.presents.server;
@@ -254,12 +254,6 @@ public class PresentsDObjectMgr
*/
protected void processEvent (DEvent event)
{
// handle graceful shutdown
if (event instanceof ShutdownEvent) {
_running = false;
return;
}
// look up the target object
DObject target = (DObject)_objects.get(event.getTargetOid());
if (target == null) {
@@ -325,14 +319,17 @@ public class PresentsDObjectMgr
}
/**
* Requests that the dobjmgr shut itself down. It will exit the event
* processing loop which cause <code>run()</code> to return.
* Requests that the dobjmgr shut itself down soon- you may
* want to try using {@link Invoker#shutdown} which will make sure that
* both the Invoker and DObjectMgr are empty and then shut them both down.
*/
public void shutdown ()
public void harshShutdown ()
{
// stick a bogus object on the event queue to ensure that the mgr
// wakes up and smells the coffee
_evqueue.append(new ShutdownEvent());
postUnit(new Runnable() {
public void run () {
_running = false;
}
});
}
/**
@@ -568,6 +565,15 @@ public class PresentsDObjectMgr
return true;
}
/**
* Should not need to be called except by the invoker during shutdown
* to ensure that things are proceeding smoothly.
*/
public boolean queueIsEmpty ()
{
return !_evqueue.hasElements();
}
protected synchronized boolean isRunning ()
{
return _running;
@@ -768,21 +774,6 @@ public class PresentsDObjectMgr
protected int _action;
}
protected class ShutdownEvent extends DEvent
{
public ShutdownEvent ()
{
super(0); // target the bogus object
}
public boolean applyToObject (DObject target)
throws ObjectAccessException
{
// nothing doing!
return false;
}
}
/**
* Registers our event helper methods.
*/
@@ -1,5 +1,5 @@
//
// $Id: PresentsServer.java,v 1.34 2003/03/31 04:11:01 mdb Exp $
// $Id: PresentsServer.java,v 1.35 2003/08/08 03:11:55 ray Exp $
package com.threerings.presents.server;
@@ -247,8 +247,8 @@ public class PresentsServer
});
// finally shut down the invoker and distributed object manager
// (The invoker does both for us.)
invoker.shutdown();
omgr.shutdown();
}
/**
@@ -1,5 +1,5 @@
//
// $Id: Invoker.java,v 1.7 2003/04/10 21:04:55 mdb Exp $
// $Id: Invoker.java,v 1.8 2003/08/08 03:11:55 ray Exp $
package com.threerings.presents.util;
@@ -115,55 +115,47 @@ public class Invoker extends LoopingThread
public void iterate ()
{
// pop the next item off of the queue
Object unit = _queue.get();
Unit unit = (Unit) _queue.get();
// if it's a unit, we invoke it
if (unit instanceof Runnable) {
long start;
long start;
if (PERF_TRACK) {
start = System.currentTimeMillis();
_unitsRun++;
}
try {
if (unit.invoke()) {
// if it returned true, we post it to the dobjmgr
// thread to invoke the result processing
_omgr.postUnit(unit);
}
// track some performance metrics
if (PERF_TRACK) {
start = System.currentTimeMillis();
_unitsRun++;
long duration = System.currentTimeMillis() - start;
Object key = unit.getClass();
Histogram histo = (Histogram)_tracker.get(key);
if (histo == null) {
// track in buckets of 50ms up to 500ms
_tracker.put(key, histo = new Histogram(0, 50, 10));
}
histo.addValue((int)duration);
}
try {
if (((Unit)unit).invoke()) {
// if it returned true, we post it to the dobjmgr
// thread to invoke the result processing
_omgr.postUnit((Runnable)unit);
}
// track some performance metrics
if (PERF_TRACK) {
long duration = System.currentTimeMillis() - start;
Object key = unit.getClass();
Histogram histo = (Histogram)_tracker.get(key);
if (histo == null) {
// track in buckets of 50ms up to 500ms
_tracker.put(key, histo = new Histogram(0, 50, 10));
}
histo.addValue((int)duration);
}
} catch (Exception e) {
Log.warning("Invocation unit failed [unit=" + unit + "].");
Log.logStackTrace(e);
}
} else {
// if it's not a runnable, it was just an object posted to our
// queue to wake us up and tell us to go away
_running = false;
} catch (Exception e) {
Log.warning("Invocation unit failed [unit=" + unit + "].");
Log.logStackTrace(e);
}
}
// documentation inherited
/**
* Will do a sophisticated shutdown of both itself and the DObjectManager
* thread.
*/
public void shutdown ()
{
// we do some custom shutdown business: add a non-runnable to the
// queue which, when the invoker gets to it, will cause it to shut
// itself down
_queue.append(new Integer(0));
_queue.append(new ShutdownUnit());
}
// documentation inherited from interface
@@ -182,6 +174,60 @@ public class Invoker extends LoopingThread
}
}
/**
* This unit gets posted back and forth between the invoker and DObjectMgr
* until both of their queues are empty and they can both be safely
* shutdown.
*/
protected class ShutdownUnit extends Unit
{
// run on the invoker thread
public boolean invoke ()
{
// if the invoker queue is not empty, we put ourselves back on it
if (_queue.hasElements()) {
System.err.println("invoker not empty");
postUnit(this);
return false;
} else {
// the invoker is empty, let's go over to the omgr
System.err.println("invoker empty, passing");
_passCount++;
return true;
}
}
// run on the dobj thread
public void handleResult ()
{
// if the queue isn't empty, re-post
if (!_omgr.queueIsEmpty()) {
_omgr.postUnit(this);
// if both queues are empty, or we've passed 50 times, end it all
} else if (!_queue.hasElements() || (_passCount >= 50)) {
// shut it down!
_omgr.harshShutdown(); // end the dobj thread
// and since we're ending the invoker from the dobjmgr
// we need to post one last event to the invoker
postUnit(new Unit() {
public boolean invoke () {
_running = false; // end this thread
return false;
}
});
// otherwise, we need to pass back to the invoker
} else {
postUnit(this);
}
}
/** The number of times we've been passed to the object manager. */
protected int _passCount = 0;
}
/** The invoker's queue of units to be executed. */
protected Queue _queue = new Queue();