com.publicobject.misc.util.concurrent.JobQueue Maven / Gradle / Ivy
/* Glazed Lists (c) 2005 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
/* StarLight Systems */
package com.publicobject.misc.util.concurrent;
import java.util.List;
import java.util.ArrayList;
/**
* A simple task queue for writing multithreaded tests.
*
* This has special logic to maintain the thread interrupted state between
* tasks. If the thread was interrupted at the end of one task, it will stay
* interrupted at the start of the next task.
*
* @author Jesse Wilson
*/
public class JobQueue implements Runnable {
/** the tasks to invoke, in order of invocation */
private List jobs = new ArrayList();
@Override
public void run() {
boolean interrupted = false;
while(true) {
// find a job to run, if any
Job job = null;
synchronized(this) {
if(!jobs.isEmpty()) {
job = jobs.remove(0);
} else {
try {
wait();
} catch(InterruptedException e) {
// interrupt the next execution
interrupted = true;
}
continue;
}
}
// run the job
try {
if(interrupted) {
Thread.currentThread().interrupt();
}
job.runnable.run();
interrupted = Thread.interrupted();
} catch(Throwable t) {
synchronized(job) {
job.thrown = t;
}
} finally {
synchronized(job) {
job.done = true;
job.notify();
}
}
}
}
/**
* Invoke the specified job and return immediately.
*/
public Job invokeLater(Runnable runnable) {
// queue the job for execution
Job job = new Job(runnable);
synchronized(this) {
jobs.add(job);
notify();
}
return job;
}
/**
* Invoke the specified job on the other thread and wait for it to complete.
*/
public void invokeAndWait(Runnable runnable) {
// queue the job for execution
Job job = new Job(runnable);
synchronized(this) {
jobs.add(job);
notify();
}
// wait for the job to complete
synchronized(job) {
while(!job.done) {
try {
job.wait();
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
}
// if the job threw anything, throw that
job.rethrowIfNecessary();
}
public void flush() {
// flush the queue
invokeAndWait(new NoOpRunnable());
}
private class NoOpRunnable implements Runnable {
@Override
public void run() {
// do nothing
}
}
/**
* A task and its result.
*/
public static class Job {
private final Runnable runnable;
private boolean done = false;
private Throwable thrown = null;
public Job(Runnable runnable) {
this.runnable = runnable;
}
public void rethrowIfNecessary() {
if(thrown == null) return;
if(thrown instanceof RuntimeException) throw (RuntimeException)thrown;
if(thrown instanceof Error) throw (Error)thrown;
throw new IllegalStateException("Unexpected throwable " + thrown.getClass());
}
public Throwable getThrown() {
return thrown;
}
}
}