// // $Id: Invoker.java,v 1.2 2001/08/14 06:48:31 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 = new Queue(); }