Ripped out the guts of IntervalManager and replaced them with Java 1.4's
java.util.Timer. We might as well use the standard stuff, and why not: it's got a snazzy priority heap for scheduling. However, we still like the flexibility of IntervalManager's static methods and the Interval interface (Timer's helper, TimerTask, is a class. Boo.). Note: I ripped out so many guts that we no longer have the ability to use a pool of helper threads for expiring Intervals. We never used it anyway. If we desire it in the future, it'd be easy enough to put back in. git-svn-id: https://samskivert.googlecode.com/svn/trunk@757 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: IntervalManager.java,v 1.5 2002/02/19 03:39:41 mdb Exp $
|
// $Id: IntervalManager.java,v 1.6 2002/05/23 23:37:50 ray Exp $
|
||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001 Michael Bayne
|
// Copyright (C) 2001 Michael Bayne
|
||||||
@@ -20,58 +20,22 @@
|
|||||||
|
|
||||||
package com.samskivert.util;
|
package com.samskivert.util;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import com.samskivert.Log;
|
import com.samskivert.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interval: can be used to register an object that is to be called once
|
* Interval: can be used to register an object that is to be called once
|
||||||
* or repeatedly after an interval has expired.
|
* or repeatedly after an interval has expired.
|
||||||
*
|
*
|
||||||
* <p> A caveat: be careful about deadlocks kids. The number of helper
|
* This now uses java.util.Timer to do all the actual scheduling, but we
|
||||||
* threads should exceed the number of times you nest calls to Interval
|
* keep this front end because it is static (accessable anywhere)
|
||||||
* stuff from inside the intervalExpired method of an Interval. Normally,
|
* and because Interval is an interface, unlike TimerTask, which is an
|
||||||
* if all you're using this for is interrupting other threads or calling
|
* abstract class.
|
||||||
* repaint you can get by with 0 helpers. If you want to do some more
|
|
||||||
* complicated stuff in intervalExpired() then you probably want a few
|
|
||||||
* threads in the pool. If you're doing extensive processing and setting
|
|
||||||
* up other timeouts on that thread then you want to make sure you've got
|
|
||||||
* plenty of threads.
|
|
||||||
*/
|
*/
|
||||||
public class IntervalManager extends Thread
|
public class IntervalManager
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Sets the maximum number of helper threads to run methods in
|
|
||||||
* Intervals. Defaults to 0, meaning that the main thread does all
|
|
||||||
* the work. Setting this to a nonzero value makes it such that a
|
|
||||||
* pool of helper threads is created to do all the actual work.
|
|
||||||
*/
|
|
||||||
public static synchronized void setMaxHelperThreads (int newmax)
|
|
||||||
{
|
|
||||||
newmax = Math.max(newmax, 0);
|
|
||||||
|
|
||||||
// if we are moving down, we need to kill some threads.
|
|
||||||
if (_helpers > newmax) {
|
|
||||||
for (int ii=0; ii < _helpers - newmax; ii++) {
|
|
||||||
_queue.append(KILLHELPER);
|
|
||||||
}
|
|
||||||
_helpers = newmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally, set the new maximum thread pool size.
|
|
||||||
_maxhelpers = newmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment the number of maximum helper threads. This is useful if
|
|
||||||
* you may have many packages which use the IntervalMgr as a
|
|
||||||
* threadpool to do work, each package can statically initialize the
|
|
||||||
* number of threads they want to be available by calling this method
|
|
||||||
* and they'll all be added together.
|
|
||||||
*/
|
|
||||||
public static synchronized void incrementHelperThreads (int amt)
|
|
||||||
{
|
|
||||||
setMaxHelperThreads(_maxhelpers + amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule the intervaled object to get called after an interval.
|
* Schedule the intervaled object to get called after an interval.
|
||||||
*
|
*
|
||||||
@@ -87,16 +51,33 @@ public class IntervalManager extends Thread
|
|||||||
public static int register (Interval i, long delay, Object arg,
|
public static int register (Interval i, long delay, Object arg,
|
||||||
boolean recur)
|
boolean recur)
|
||||||
{
|
{
|
||||||
synchronized (_mgr) {
|
IntervalTask task = new IntervalTask(i, arg, recur);
|
||||||
IntervalItem item = new IntervalItem(i, delay, arg, recur);
|
_hash.put(task.id, task);
|
||||||
_hash.put(item.id, item);
|
|
||||||
_schedule.add(item);
|
if (recur) {
|
||||||
_schedule.sort();
|
_timer.scheduleAtFixedRate(task, delay, delay);
|
||||||
if (item.endtime < _nextwake) {
|
} else {
|
||||||
_mgr.notify();
|
_timer.schedule(task, delay);
|
||||||
}
|
}
|
||||||
return item.id;
|
|
||||||
}
|
return task.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule the interval to be called repeatedly, but at a fixed delay
|
||||||
|
* instead of fixed rate.
|
||||||
|
* This means that the delay between executions will be the
|
||||||
|
* delay specified below plus the actual execution time it takes
|
||||||
|
* to run the interval's expire method.
|
||||||
|
*/
|
||||||
|
public static int registerAtFixedDelay (Interval i, long delay, Object arg)
|
||||||
|
{
|
||||||
|
IntervalTask task = new IntervalTask(i, arg, true);
|
||||||
|
_hash.put(task.id, task);
|
||||||
|
|
||||||
|
_timer.schedule(task, delay, delay);
|
||||||
|
|
||||||
|
return task.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,256 +88,67 @@ public class IntervalManager extends Thread
|
|||||||
*/
|
*/
|
||||||
public static void remove (int id)
|
public static void remove (int id)
|
||||||
{
|
{
|
||||||
synchronized (_mgr) {
|
IntervalTask task = (IntervalTask) _hash.remove(id);
|
||||||
IntervalItem item = (IntervalItem) _hash.remove(id);
|
if (task != null) {
|
||||||
if (item != null) {
|
task.cancel();
|
||||||
_schedule.remove(item);
|
} else {
|
||||||
return;
|
Log.warning("remove() called on non-registered " +
|
||||||
}
|
"interval [id=" + id + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.warning("remove() called on non-registered " +
|
|
||||||
"interval [id=" + id + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The timer we use to schedule everything. */
|
||||||
* Do our interval thing.
|
protected static Timer _timer = new Timer(true);
|
||||||
*/
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
while (_mgr == Thread.currentThread()) {
|
|
||||||
|
|
||||||
// check to see if an interval has expired, if so call
|
|
||||||
// expired from an unsynchronized position.
|
|
||||||
// TrackedThread.setState("Checking intervals");
|
|
||||||
IntervalItem item = checkInterval();
|
|
||||||
if (item != null) {
|
|
||||||
if (_maxhelpers > 0) {
|
|
||||||
helperHandle(item);
|
|
||||||
} else {
|
|
||||||
item.expired(); // we have no helpers, do it ourselves.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now attempt to sleep
|
|
||||||
// TrackedThread.setState("Waiting for Interval event...");
|
|
||||||
doWait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Have a helper thread handle the expiration of this interval.
|
|
||||||
*/
|
|
||||||
protected static synchronized void helperHandle (IntervalItem item)
|
|
||||||
{
|
|
||||||
// put the item on the queue...
|
|
||||||
_busyhelpers++;
|
|
||||||
_queue.append(item);
|
|
||||||
|
|
||||||
// possibly create a new thread in the pool to do this work.
|
|
||||||
if ((_busyhelpers > _helpers) && (_helpers < _maxhelpers)) {
|
|
||||||
IntervalExpirer helper = new IntervalExpirer(_queue);
|
|
||||||
_helpers++;
|
|
||||||
helper.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper thread lets us know when it has finished its work.
|
|
||||||
*/
|
|
||||||
protected static synchronized void helperFinished ()
|
|
||||||
{
|
|
||||||
_busyhelpers--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if anything needs calling.
|
|
||||||
*/
|
|
||||||
private synchronized IntervalItem checkInterval ()
|
|
||||||
{
|
|
||||||
// TrackedThread.setState("Checking intervals");
|
|
||||||
if (_schedule.size() > 0) {
|
|
||||||
IntervalItem item = (IntervalItem) _schedule.get(0);
|
|
||||||
|
|
||||||
// it's totally valid for us to wake up early..
|
|
||||||
// so make sure we really want to run the first item on the queue.
|
|
||||||
if (item.endtime <= System.currentTimeMillis()) {
|
|
||||||
// we gotta deal with this monster!
|
|
||||||
// first, remove it.
|
|
||||||
_schedule.remove(0);
|
|
||||||
if (item.checkRecur()) {
|
|
||||||
// since we're definitionally not sleeping, we can just
|
|
||||||
// insert into the queue without checking wait times..
|
|
||||||
_schedule.add(item);
|
|
||||||
_schedule.sort();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// otherwise, get rid of this interval altogether
|
|
||||||
_hash.remove(item.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sleep until the next Interval needs to be attended to.
|
|
||||||
*/
|
|
||||||
private synchronized void doWait ()
|
|
||||||
{
|
|
||||||
// TrackedThread.setState("Waiting for Interval event...");
|
|
||||||
if (_schedule.size() == 0) {
|
|
||||||
_nextwake = Long.MAX_VALUE;
|
|
||||||
} else {
|
|
||||||
IntervalItem item = (IntervalItem) _schedule.get(0);
|
|
||||||
_nextwake = item.endtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
long waittime = _nextwake - System.currentTimeMillis();
|
|
||||||
if (waittime > 0L) {
|
|
||||||
try {
|
|
||||||
wait(waittime);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntervalManager ()
|
|
||||||
{
|
|
||||||
super("IntervalManager");
|
|
||||||
setDaemon(true);
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// there can be only one!
|
|
||||||
protected static IntervalManager _mgr = new IntervalManager();
|
|
||||||
|
|
||||||
// We just use a sorted list for now since we aren't likely to have
|
|
||||||
// too many monitorables at any time. Insertions are O(log n),
|
|
||||||
// removals are O(n) since we search then entire queue to find the
|
|
||||||
// intervaleds to remove.
|
|
||||||
//
|
|
||||||
// If we someday find that we have a lot of intervaleds, we may want
|
|
||||||
// to rewrite this such that we can add and remove intervaleds much
|
|
||||||
// faster.
|
|
||||||
protected static SortableArrayList _schedule = new SortableArrayList();
|
|
||||||
|
|
||||||
|
/** Our registered intervals, indexed by id. */
|
||||||
protected static HashIntMap _hash = new HashIntMap();
|
protected static HashIntMap _hash = new HashIntMap();
|
||||||
protected static long _nextwake = Long.MAX_VALUE;
|
|
||||||
|
|
||||||
protected static int _helpers = 0; // # of created helpers
|
/**
|
||||||
protected static int _maxhelpers = 0; // max # of helpers we can create
|
* A class to adapt TimerTask to the smooth action of Interval.
|
||||||
protected static int _busyhelpers = 0; // # of outstanding requests
|
*/
|
||||||
protected static Queue _queue = new Queue();
|
static class IntervalTask extends TimerTask
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
protected Interval _i;
|
||||||
|
protected Object _arg;
|
||||||
|
protected boolean _onetime;
|
||||||
|
|
||||||
protected static final Object KILLHELPER = new Object();
|
public IntervalTask (Interval i, Object arg, boolean recur)
|
||||||
}
|
{
|
||||||
|
_i = i;
|
||||||
class IntervalItem implements Comparable
|
_arg = arg;
|
||||||
{
|
_onetime = !recur;
|
||||||
public long endtime;
|
id = nextID();
|
||||||
public int id;
|
}
|
||||||
|
|
||||||
protected long _timeout;
|
/**
|
||||||
protected boolean _recur;
|
* Run the interval. It's synchronized so that if we someday have
|
||||||
protected Interval _i;
|
* multiple threads, the same interval won't be called multiple times.
|
||||||
protected Object _arg;
|
*/
|
||||||
|
public void run ()
|
||||||
public IntervalItem (Interval i, long timeout, Object arg, boolean recur)
|
{
|
||||||
{
|
// protect our ass.
|
||||||
_i = i;
|
try {
|
||||||
_timeout = Math.max(timeout, 0);
|
_i.intervalExpired(id, _arg);
|
||||||
_arg = arg;
|
} catch (Exception e) {
|
||||||
_recur = recur;
|
Log.warning("Exception while expiring interval: " + e);
|
||||||
|
Log.logStackTrace(e);
|
||||||
id = nextID();
|
}
|
||||||
endtime = System.currentTimeMillis() + timeout;
|
|
||||||
}
|
// if we're not recurring, be sure to remove from the mgr's hash
|
||||||
|
if (_onetime) {
|
||||||
public int compareTo (Object other)
|
_hash.remove(id);
|
||||||
{
|
}
|
||||||
return (int) (endtime - ((IntervalItem) other).endtime);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Unique ids for each interval item.
|
||||||
* Test-n-set recurring stuff. If we do recur, increment the time we
|
*/
|
||||||
* are to next wake.
|
private static synchronized int nextID ()
|
||||||
*/
|
{
|
||||||
public boolean checkRecur ()
|
return _idseq++;
|
||||||
{
|
}
|
||||||
if (_recur) {
|
|
||||||
endtime += _timeout;
|
private static int _idseq = 0;
|
||||||
}
|
}
|
||||||
return _recur;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the interval. It's synchronized so that if we someday have
|
|
||||||
* multiple threads, the same interval won't be called multiple times.
|
|
||||||
*/
|
|
||||||
public synchronized void expired ()
|
|
||||||
{
|
|
||||||
// protect our ass.
|
|
||||||
try {
|
|
||||||
_i.intervalExpired(id, _arg);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Exception while expiring interval: " + e);
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For debugging.
|
|
||||||
*/
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "Interval: " + _i + " (wakes in " +
|
|
||||||
(endtime - System.currentTimeMillis()) + "ms)" +
|
|
||||||
(_recur ? (" [recurring every " + _timeout + "ms]") : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unique ids for each interval item.
|
|
||||||
*/
|
|
||||||
private static synchronized int nextID ()
|
|
||||||
{
|
|
||||||
return _idseq++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int _idseq = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These are the helper threads for the IntervalManager.
|
|
||||||
*/
|
|
||||||
class IntervalExpirer extends Thread
|
|
||||||
{
|
|
||||||
public IntervalExpirer (Queue queue)
|
|
||||||
{
|
|
||||||
super("IntervalExpirer");
|
|
||||||
setDaemon(true);
|
|
||||||
_queue = queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
// TrackedThread.setState("Waiting for Interval to run...");
|
|
||||||
Object o = _queue.get();
|
|
||||||
if (o == IntervalManager.KILLHELPER) {
|
|
||||||
break; //exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise run the bashtard!
|
|
||||||
((IntervalItem) o).expired();
|
|
||||||
|
|
||||||
IntervalManager.helperFinished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Queue _queue;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user