
org.monte.media.concurrent.EventLoop Maven / Gradle / Ivy
/*
* @(#)EventLoop.java 1.5 2011-02-17
*
* Copyright (c) 2001-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.concurrent;
import java.util.*;
/**
* An EventLoop can process events on a separate worker thread.
* It consists of two parts: the event collector and the event
* processor.
*
* The event collector collects all incoming events and puts them
* into a queue.
* The event processor removes the events from the queue and
* processes them.
*
* The key feature of the EventLoop is, that clients don't have
* to wait until an event has been processed. Clients are free
* to proceed as soon as the collector has put the event into
* the queue.
*
* Usage
*
* This is an abstract class. It does all the queue handling, but
* does no processing. To use it, you have to create a subclass
* which overrides the methods #collectEvent and #processEvent.
*
* Example
*
* An EventLoop, which outputs Strings on a background thread
* could look like this:
*
* public class AsyncDisplay
* extends AbstractEventLoop {
* public void display(String string) {
* collectEvent(string);
* }
* protected void processEvent(Object event) {
* System.out.println((String) event);
* }
* }
*
*
* To use the class proceed like this:
*
* AsyncDisplay a = new AsyncDisplay();
* a.display("Hello World");
*
*
* @author Werner Randelshofer
* @version 1.5 2011-02-17 Merged with CubeTwister.
*
1.4 2008-01-02 Added method join.
*
1.3 2002-05-15 Reworked.
*
1.2 2001-09-24 Support for coalescing of events added.
*
1.1 2001-08-24 Reworked for JDK 1.3.
*
1.0.2 2000-03-03
* Catch SecurityException's (To make this class work in Netscape Navigator).
*
1.0.1 1998-10-14 Adds version info to class comments.
*
1.0 1998-10-01 Threads are not reused anymore.
*/
public abstract class EventLoop {
/**
* The event processor thread.
*/
protected Thread eventProcessor;
/**
* The priority of the processor thread.
*/
private int priority;
/**
* The queue stores the events until they
* can be processed by a processor thread.
*/
private final Vector queue = new Vector();
/**
* Indicates whether multiple events will be coalesced
* by the event processor or not.
*/
private boolean isCoalesce;
/**
* Indicates whether events will be processed by the
* event processor or not.
*/
private volatile boolean isAlive = true;
/**
* Creates a new EventLoop which processes events at Thread.NORM_PRORITY.
*/
public EventLoop() {
this(Thread.NORM_PRIORITY);
}
/**
* Creates a new EventLoop which processes events at the
* desired thread priority.
*
* @param priority The Thread priority of the event processor.
*/
public EventLoop(int priority) {
this.priority = priority;
}
/**
* Collects an event and puts it into the event queue
* for later processing.
*
* @param event The event to be put into the queue.
*/
protected void collectEvent(Object event) {
synchronized(queue) {
if (! isCoalesce || ! queue.contains(event)) {
queue.addElement(event);
if (isAlive) startProcessor();
}
}
}
/**
* Sets whether the EventLoop coalesces multiple pending events.
* A busy application may not be able to keep up with event
* generation, causing multiple events to be queued.
* Coalescing is based on equality tests of event objects.
* More formally, coalesces an event o if and only if the queue
* contains at least one element e such that
* (o==null ? e==null : o.equals(e))
.
*
* EventLoops do not coalesce events by default.
*
* @param b Specify true to turn on coalescing.
*/
public void setCoalesce(boolean b) {
isCoalesce = b;
}
/**
* Returns true if the EventLoop
* coalesces multiple pending events.
*
* @see #setCoalesce(boolean)
*/
public boolean isCoalesce() {
return isCoalesce;
}
/**
* Starts the event processor.
*
The event processor is started by default.
*/
public void start() {
synchronized(queue) {
isAlive = true;
startProcessor();
}
}
/**
* Stops the event processor.
*/
public void stop() {
isAlive = false;
}
public void join() throws InterruptedException {
Thread t = eventProcessor;
if (t != null) {
t.join();
}
}
/**
* Clears the event queue.
*/
public void clear() {
synchronized(queue) {
queue.removeAllElements();
}
}
/**
* This is the method which really starts the processor.
*/
private void startProcessor() {
synchronized(queue) {
if (eventProcessor == null) {
eventProcessor = new Thread(this+" Event Processor") {
public void run() {
processEvents();
}
};
try {
// The event processor must not be a daemon.
// If it was a daemon, the virtual machine would
// stop before the event had been processed.
eventProcessor.setDaemon(false);
} catch (SecurityException e) {}
try {
eventProcessor.setPriority(priority);
} catch (SecurityException e) {}
eventProcessor.start();
}
}
}
/**
* This method processes a single event on the event processor thread.
*
* @param event An event from the queue.
*/
protected abstract void processEvent(Object event);
/**
* This method removes events from the event queue
* and proceses them until the queue is empty or
* until #stop is called.
*
* This method must be called from the event processor
* thread only.
*/
protected void processEvents() {
Object event;
while (isAlive) {
synchronized(queue) {
if (queue.isEmpty()) {
eventProcessor = null;
return;
}
event = queue.elementAt(0);
queue.removeElementAt(0);
}
try {
processEvent(event);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}