test.ca.odell.glazedlists.ThreadedCompositeRunner Maven / Gradle / Ivy
Show all versions of glazedlists_java15 Show documentation
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists;
// Java collections are used for underlying data storage
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
/**
* The ThreadedCompositeRunner is a manually-executed test that verifies that
* the CompositeList is thread safe. It creates a handful of threads that write
* and read composite lists.
*
* To verify that deadlocks are not occuring, each thread updates its time variable
* with the current time. One extra thread cycles through examining the clock
* variables and prints the time of the oldest thread. If such a variable never
* increments, we have a deadlock!
*
* @author Kevin Maltby
*/
public class ThreadedCompositeRunner {
/** the global clock of the threaded composite runner */
private int clock = 0;
/**
* A clocked thread is a thread that records the most recent time
* it performed an operation.
*/
abstract class ClockedThread implements Runnable {
private int time = 0;
public int getTime() {
synchronized(ThreadedCompositeRunner.this) {
return time;
}
}
protected void stampTime() {
synchronized(ThreadedCompositeRunner.this) {
time = clock;
}
}
}
/**
* The reader thread alternates between reading a single random value
* and reading the entire list.
*/
class ReaderThread extends ClockedThread {
private EventList listToRead;
private Random dice = new Random();
public ReaderThread(EventList listToRead) {
this.listToRead = listToRead;
}
public void run() {
while(true) {
// read one element
listToRead.getReadWriteLock().readLock().lock();
if(listToRead.size() > 0) {
listToRead.get(dice.nextInt(listToRead.size()));
}
listToRead.getReadWriteLock().readLock().unlock();
// read all elements
listToRead.getReadWriteLock().readLock().lock();
listToRead.toArray();
listToRead.getReadWriteLock().readLock().unlock();
// stamp the time
stampTime();
// yield and loop
Thread.yield();
}
}
}
/**
* The writer thread alternates between inserting a single random value
* and removing a single random value.
*/
class WriterThread extends ClockedThread {
private EventList listToWrite;
private Random dice = new Random();
public WriterThread(EventList listToWrite) {
this.listToWrite = listToWrite;
}
public void run() {
while(true) {
int repeat = dice.nextInt(1000);
for(int i = 0; i < repeat; i++) {
listToWrite.add(dice.nextInt(i + 1), Boolean.TRUE);
// stamp the time
stampTime();
// yield and loop
Thread.yield();
}
while(!listToWrite.isEmpty()) {
listToWrite.remove(dice.nextInt(listToWrite.size()));
// stamp the time
stampTime();
// yield and loop
Thread.yield();
}
}
}
}
/**
* The main method starts this experiment.
*/
public static void main(String[] args) {
new ThreadedCompositeRunner().run(args);
}
/**
* Starts a series of reader and writer threads and verifies that they
* continue reading and writing.
*/
public void run(String[] args) {
if(args.length != 3) {
System.out.println("Usage: ThreadedCompositeRunner ");
System.out.println("");
System.out.println("writer count: The number of writer threads, each writing their own BasicEventList");
System.out.println("reader count: The number of reader threads, each reading a CompositeList");
System.out.println("composite size: The number of writer lists per CompositeList");
System.out.println("");
System.out.println("The application starts threads for the writers and readers. Then one extra thread");
System.out.println("called the coordinator asks each thread the time that they last completed an");
System.out.println("operation. This is equivalent to asking each thread whether it has deadlocked yet.");
System.out.println("A thread that has deadlocked will have a last-operation-time value that never");
System.out.println("increments. The coordinator prints the oldest last-operation-time and the current");
System.out.println("time. If the oldest last-operation-time has not incremented since last check, the");
System.out.println("coordinator only prints the current time. A user can tell that a thread has deadlocked");
System.out.println("if the clock value is printed repeatedly but the oldest last-operation-time no longer");
System.out.println("increments.");
return;
}
int writerCount = Integer.parseInt(args[0]);
int readerCount = Integer.parseInt(args[1]);
int compositeSize = Integer.parseInt(args[2]);
// prepare a random number generator
Random dice = new Random();
// create a list of lists
List lists = new ArrayList();
// accumulate a list of threads
List threads = new ArrayList();
// start some writer threads
for(int i = 0; i < writerCount; i++) {
BasicEventList list = new BasicEventList();
lists.add(list);
WriterThread thread = new WriterThread(list);
threads.add(thread);
}
// create some composite lists & reader threads
List compositeLists = new ArrayList();
for(int i = 0; i < readerCount; i++) {
CompositeList compositeList = new CompositeList();
for(int j = 0; j < compositeSize; j++) {
compositeList.addMemberList((EventList)lists.get(dice.nextInt(lists.size())));
}
compositeLists.add(compositeList);
// create a reader thread for this composite list
ReaderThread readerThread = new ReaderThread(compositeList);
threads.add(readerThread);
}
// start the threads
int threadId = 100;
for(Iterator i = threads.iterator(); i.hasNext(); ) {
ClockedThread runnable = (ClockedThread)i.next();
new Thread(runnable, "T" + threadId++).start();
}
// watch the times
int lastMinTime = -1;
while(true) {
// find the minimum time of all threads
int minTime = clock;
for(Iterator i = threads.iterator(); i.hasNext(); ) {
ClockedThread thread = (ClockedThread)i.next();
int threadTime = thread.getTime();
if(threadTime < minTime) minTime = threadTime;
}
// print the min time if it has changed
if(minTime > lastMinTime) {
System.out.println("CLOCK " + clock + " MINTIME " + minTime);
lastMinTime = minTime;
} else {
if(clock % 100 == 0) System.out.println("CLOCK " + clock);
}
// increment the clock
synchronized(ThreadedCompositeRunner.this) {
clock++;
}
// yield and loop
Thread.yield();
}
}
}