From eb6de7d075e9435e4810e0e2324110485fb97729 Mon Sep 17 00:00:00 2001 From: mdb Date: Tue, 29 Jun 2004 03:14:22 +0000 Subject: [PATCH] Refactored the Invoker into a generic class which PresentsServer extends to do its custom fiddly business. git-svn-id: https://samskivert.googlecode.com/svn/trunk@1454 6335cc39-0255-0410-8fd6-9bcaacd3b74c --- .../src/java/com/samskivert/util/Invoker.java | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 projects/samskivert/src/java/com/samskivert/util/Invoker.java diff --git a/projects/samskivert/src/java/com/samskivert/util/Invoker.java b/projects/samskivert/src/java/com/samskivert/util/Invoker.java new file mode 100644 index 00000000..dcfba007 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/util/Invoker.java @@ -0,0 +1,189 @@ +// +// $Id: Invoker.java,v 1.1 2004/06/29 03:14:22 mdb Exp $ + +package com.samskivert.util; + +import java.util.HashMap; +import java.util.Iterator; + +import com.samskivert.Log; + +/** + * 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 main thread. + * + *

The invoker is a useful tool for services that need to block and + * therefore cannot be run on the main thread. For example, an interactive + * application 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 +{ + /** An interface by which the invoker communicates back to the entity + * that manages the main thread. */ + public static interface ResultReceiver + { + /** Posts an unit that should be executed on the main thread. */ + public void postUnit (Runnable unit); + } + + /** + * 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 main thread once the primary code has completed on + * the invoker thread. + */ + public static abstract class Unit implements Runnable + { + /** The default constructor. */ + public Unit () {} + + /** Creates an invoker unit which will report the supplied name in + * {@link #toString}. */ + public Unit (String name) + { + _name = name; + } + + /** + * 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 regular world) or false to indicate that no + * further processing should be performed. + * + * @return true if the handleResult method should be + * invoked on the main 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 main + * 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 receiver interface simple, + // 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(); + } + + /** Returns the name of this invoker. */ + public String toString () + { + return _name; + } + + protected String _name = "Unknown"; + } + + /** + * Creates an invoker that will post results to the supplied result + * receiver. + */ + public Invoker (String name, ResultReceiver receiver) + { + super(name); + _receiver = receiver; + } + + /** + * 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 + Unit unit = (Unit) _queue.get(); + + long start; + if (PERF_TRACK) { + start = System.currentTimeMillis(); + _unitsRun++; + } + + try { + if (unit.invoke()) { + // if it returned true, we post it to the receiver thread + // to invoke the result processing + _receiver.postUnit(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); + + // report long runners + if (duration > 500L) { + Log.warning("Invoker unit ran for '" + duration + "ms: " + + unit + "' (" + key + ")."); + } + } + + } catch (Exception e) { + Log.warning("Invocation unit failed [unit=" + unit + "]."); + Log.logStackTrace(e); + } + } + + /** + * Shuts down the invoker thread by queueing up a unit that will cause + * the thread to exit after all currently queued units are processed. + */ + public void shutdown () + { + _queue.append(new Unit() { + public boolean invoke () { + _running = false; + return false; + } + }); + } + + /** The invoker's queue of units to be executed. */ + protected Queue _queue = new Queue(); + + /** The result receiver with which we're working. */ + protected ResultReceiver _receiver; + + /** Tracks the counts of invocations by unit's class. */ + protected HashMap _tracker = new HashMap(); + + /** The total number of invoker units run since the last report. */ + protected int _unitsRun; + + /** Whether or not to track invoker unit performance. */ + protected static final boolean PERF_TRACK = true; +}