// EventManager.java
// $Id: EventManager.java,v 1.4 1998/12/08 10:42:11 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
// Origamist: http://www.ms.mff.cuni.cz/~peckam/origamist
//
// Copyright © 2011 World Wide Web Consortium, (Massachusetts Institute of Technology, European Research Consortium for
// Informatics and Mathematics, Keio University). All Rights Reserved. This work is distributed under the W3C® Software
// License [1] in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
/**
* A timer handling package.
* This package was written by J Payne.
*
* 20 May 2011 edited by Martin Pecka; changed Vector to Vector<Event>
*/
package org.w3c.tools.timers;
import java.util.Vector;
class Event
{
/** Absolute time, in ms, to deliver this event. */
long time;
/** Piece of data to pass to the event handler. */
Object data;
/** handler for this event */
EventHandler handler;
Event(long time, EventHandler handler, Object data)
{
this.time = time;
this.handler = handler;
this.data = data;
}
}
/**
* This implements an event manager for timer events. Timer events
* are a way to have events occur some time in the future. They are an
* alternative to using separate threads which issue sleep requests
* themselves.
*/
public class EventManager extends Thread implements EventHandler
{
static final boolean debug = false;
/** Vector of events, sorted by time. */
Vector<Event> queue = new Vector<Event>();
public EventManager()
{
setName("Event Manager");
}
/**
* registerTimer inserts a new timer event into the queue. The
* queue is always sorted by time, in increasing order. That is,
* things farther into the future are further down in the queue.
*
* ms is milliseconds in the future, handler is the object that
* will handle the event, and data is a "rock" that is passed to
* the handler to do with what it will.
*
* This returns an opaque object which can be used to recall the
* timer before it is delivered.
*/
public Object registerTimer(long ms, EventHandler handler, Object data)
{
long time = ms + System.currentTimeMillis();
Event event = new Event(time, handler, data);
return registerTimer(event);
}
boolean done = false;
public synchronized void stopEventManager()
{
done = true;
notify();
}
/**
* This is like the above registerTimer, except it takes an event
* object with the deliver time filled in. If deliver time is
* before the current time, the event is "immediately" delivered.
* Do a binary search to figure out where the event goes.
*/
public synchronized Object registerTimer(Event newEvent)
{
int lo = 0;
int hi = queue.size();
long newTime = newEvent.time;
Event e;
long midTime;
if (done) {
return null;
}
if (hi == 0) {
queue.addElement(newEvent);
} else {
while (hi - lo > 0) {
int mid = (hi + lo) >> 1;
e = (Event) queue.elementAt(mid);
midTime = e.time;
if (midTime < newTime) {
lo = mid + 1;
} else if (midTime > newTime) {
hi = mid;
} else {
lo = mid;
break;
}
}
if (lo < hi && ((Event) queue.elementAt(lo)).time > newTime) {
lo += 1;
}
queue.insertElementAt(newEvent, lo);
}
if (debug) {
checkQueue();
}
notify();
return newEvent;
}
/** This recalls a previously registered timer event. */
public synchronized Object recallTimer(Object timer)
{
int lo = 0;
int hi = queue.size();
int limit = hi;
long destTime = ((Event) timer).time;
Event e;
long midTime;
if (hi == 0) {
return null; /* already delivered or recalled */
}
while (hi - lo > 0) {
int mid = (hi + lo) >> 1;
e = (Event) queue.elementAt(mid);
midTime = e.time;
if (midTime < destTime) {
lo = mid + 1;
} else if (midTime > destTime) {
hi = mid;
} else {
lo = mid;
for (int i = mid - 1; i >= 0; i--) {
e = (Event) queue.elementAt(i);
if (e.time == midTime) {
lo = i;
} else {
break;
}
}
break;
}
}
while (lo < limit) {
e = (Event) queue.elementAt(lo);
if (e.time == destTime) {
if (e == timer) {
queue.removeElementAt(lo);
break;
} else {
lo += 1;
}
} else {
return null;
}
}
if (debug) {
checkQueue();
}
if (lo == 0)
notify();
return timer;
}
/**
* This is a separate method so that it can be
* synchronized while the actual execution of the event
* does not lock the EventManager. In other words, while
* one event is being processed by its handler, others can
* register or recall other events.
*/
synchronized Event getNextEvent()
{
while (true) {
while (queue.size() == 0) {
if (debug) {
System.out.println("Queue waiting for event.");
}
if (done) {
return null;
}
try {
wait();
} catch (InterruptedException e) {}
}
Event e = (Event) queue.elementAt(0);
long now = System.currentTimeMillis();
long dt;
dt = e.time - now;
if (dt <= 0) {
queue.removeElementAt(0);
return e;
}
/*
* If we get here, we have no events scheduled for a while, so
* we sleep until the next event needs delivering. When we
* wake up, it could be because the queue has been changed.
* So we have to check again.
*/
if (debug) {
System.out.println("Queue sleeping for " + dt + "ms");
}
try {
wait(dt);
} catch (InterruptedException ex) {}
}
}
public void run()
{
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
while (true) {
Event e = getNextEvent();
if (done) {
break;
}
try {
e.handler.handleTimerEvent(e.data, e.time);
} catch (Exception ex) {
System.out.println("Exception " + ex + " in EventManager");
ex.printStackTrace();
} catch (Error er) {
System.out.println("Error " + er + " in EventManager");
er.printStackTrace();
}
}
}
/* This is here for testing purposes only. */
public void handleTimerEvent(Object rock, long time)
{
long dt = (System.currentTimeMillis() - time);
System.out.println("Handling event with dt=" + dt);
}
public static void main(String args[])
{
EventManager mgr = new EventManager();
int i;
for (i = 0; i < 30; i++) {
mgr.registerTimer(1000 + (int) (1000 * Math.random()), mgr, null);
}
mgr.checkQueue();
}
synchronized void checkQueue()
{
Vector<Event> q = queue;
if (q.size() == 0)
return;
Event e = (Event) q.elementAt(0);
int i, size = q.size();
for (i = 1; i < size; i++) {
Event next = (Event) q.elementAt(i);
if (next.time < e.time) {
System.out.println("Events out of order!\n");
System.out.println(q);
}
e = next;
}
}
}