diff --git a/src/java/com/threerings/presents/util/Invoker.java b/src/java/com/threerings/presents/util/Invoker.java new file mode 100644 index 000000000..3a08e9d8e --- /dev/null +++ b/src/java/com/threerings/presents/util/Invoker.java @@ -0,0 +1,128 @@ +// +// $Id: Invoker.java,v 1.1 2001/08/11 01:20:38 mdb Exp $ + +package com.threerings.cocktail.cher.util; + +import com.samskivert.util.LoopingThread; +import com.samskivert.util.Queue; + +import com.threerings.cocktail.cher.Log; +import com.threerings.cocktail.cher.server.CherServer; + +/** + * The invoker is used to invoke self-contained units of code on an + * invoking thread. Each invoker is associated with its own thread and + * that thread is used to invoke all of the units posted to that invoker + * in the order in which they were posted. The invoker also provides a + * convenient mechanism for processing the result of an invocation back on + * the dobjmgr thread. + * + *
The invoker is a useful tool for services that need to block and + * therefore cannot be run on the distributed object thread. For example, + * a user of the Cher system might provide an invoker on which to run + * database queries. + * + *
Bear in mind that each invoker instance runs units on its own
+ * thread and care must be taken to ensure that code running on separate
+ * invokers properly synchronizes access to shared information. Where
+ * possible, complete isolation of the services provided by a particular
+ * invoker is desirable.
+ */
+public class Invoker extends LoopingThread
+{
+ /**
+ * The unit encapsulates a unit of executable code that will be run on
+ * the invoker thread. It also provides facilities for additional code
+ * to be run on the dobjmgr thread once the primary code has completed
+ * on the invoker thread.
+ */
+ public static abstract class Unit implements Runnable
+ {
+ /**
+ * This method is called on the invoker thread and should be used
+ * to perform the primary function of the unit. It can return true
+ * to cause the handleResult method to be
+ * subsequently invoked on the dobjmgr thread (generally to allow
+ * the results of the invocation to be acted upon back in the
+ * context of the distributed object world) or false to indicate
+ * that no further processing should be performed.
+ *
+ *
Note that an invocation unit can do things like post events
+ * from the invoker thread (which includes modification of
+ * distributed object fields) and need not jump over to the
+ * dobjmgr thread to do so. However, it cannot read
+ * distributed object fields from the invoker thread. Any field
+ * values needed during the course of the invocation should be
+ * provided from the dobjmgr thread at the time that the
+ * invocation unit is created and posted.
+ *
+ * @return true if the handleResult method should be
+ * invoked on the dobjmgr thread, false if not.
+ */
+ public abstract boolean invoke ();
+
+ /**
+ * Invocation unit implementations can implement this function to
+ * perform any post-unit-invocation processing back on the dobjmgr
+ * thread. It will be invoked if invoke returns true.
+ */
+ public void handleResult ()
+ {
+ // do nothing by default
+ }
+
+ // we want to be a runnable to make the dobjmgr happy, but we'd
+ // like for invocation unit implementations to be able to put
+ // their result handling code into an aptly named method
+ public void run ()
+ {
+ handleResult();
+ }
+ }
+
+ /**
+ * Posts a unit to this invoker for subsequent invocation on the
+ * invoker's thread.
+ */
+ public void postUnit (Unit unit)
+ {
+ // simply append it to the queue
+ _queue.append(unit);
+ }
+
+ // documentation inherited
+ public void iterate ()
+ {
+ // pop the next item off of the queue
+ Object unit = _queue.get();
+
+ // if it's a unit, we invoke it
+ if (unit instanceof Runnable) {
+ try {
+ if (((Unit)unit).invoke()) {
+ // if it returned true, we post it to the dobjmgr
+ // thread to invoke the result processing
+ CherServer.omgr.postUnit((Runnable)unit);
+ }
+
+ } catch (Exception e) {
+ Log.warning("Invocation unit failed [unit=" + unit + "].");
+ Log.logStackTrace(e);
+ }
+ }
+
+ // if it's not a runnable, it was probably just an object posted
+ // to our queue to wake us up in time to die, so we just ignore it
+ }
+
+ // documentation inherited
+ protected void kick ()
+ {
+ // add a non-runnable to the queue which will be ignored by the
+ // invoker but will cause it to wake up and go away
+ _queue.append(new Integer(0));
+ }
+
+ /** The invoker's queue of units to be executed. */
+ protected Queue _queue;
+}