// Commented for the Learning branch
package com.limegroup.gnutella.util;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
/**
* An RRProcessingQueue keeps several lists of Runnable objects, and calls their run() methods and discards them, moving from list to list.
* A RRProcessingQueue looks like this:
*
* this name1 name2 name3 name4
* ----- ----- ----- ----- -----
* a f i m n
* b g j o
* c h k
* d l
* e
*
* Call invokeLater(h, name1) to add a new Runnable object to a named list.
* This RRProcessingQueue has a thread that is calling run() on its objects and discarding them.
* First, it will call a.run() and remove a from the first list.
* Right after that, it will call f.run() and remove f from the name1 list.
* The RRProcessingQueue's thread keeps going until it runs out of objects to run.
* If a named list becomes empty, it removes the list.
*
* The names of the lists are objects.
* The default list uses the RRProcessingQueue itself as it's name, this.
* To add a Runnable object to a RRProcessingQueue, call invokeLater(o, name4).
* If you use invokeLater(e), the method will add it to the default, this, list.
*
* RRProcessingQueue uses 3 classes: RoundRobinQueue, ProcessingQueue, and NamedQueue.
* RoundRobinQueue gives RRProcessingQueue the ability to cycle through the lists.
* Each list is a NamedQueue, which keeps Runnable objects.
* RRProcessingQueue extends ProcessingQueue, gaining the single thread that calls the run() methods.
*/
public class RRProcessingQueue extends ProcessingQueue {
/*
* A RRProcessingQueue has a HashMap named queues, and a RoundRobinQueue named lists.
* Both hold exactly the same NamedQueue objects.
* To look up a NamedQueue by its name, call queues.get(name).
* To loop through the NamedQueue objects endlessly, call lists.next().
*/
/**
* queues keeps all our lists of Runnable objects.
* Each queue is a NamedQueue, which is a list of Runnable objects and a name.
*
* A HashMap maps keys to values.
* A key is an Object that is the name of a NamedQueue.
* The value is the NamedQueue.
*/
private final Map queues = new HashMap();
/**
* The NamedQueue objects this RRProcessingQueue has.
*
* A RoundRobinQueue keeps a list of objects.
* When you call next() on it, it returns a reference to the first one, and moves it to the end.
* You can keep calling next() to loop through the objects endlessly.
*/
private final RoundRobinQueue lists = new RoundRobinQueue();
/** The total number of Runnable objects in all the named queues this RRProcessingQueue keeps. */
private int size;
/**
* Make a new RRProcessingQueue which will make a new managed or regular thread with the given name and priority.
*
* @param name The name we'll give the thread we'll make to process the items
* @param managed True to make a LimeWire ManagedThread, false to just make a regular Java thread
* @param priority The priority we'll run the thread at
*/
public RRProcessingQueue(String name, boolean managed, int priority) {
// Call the ProcessingQueue constructor
super(name, managed, priority);
}
/**
* Make a new RRProcessingQueue which will make a new managed or regular thread with the given name.
*
* @param name The name we'll give the thread we'll make to process the items
* @param managed True to make a LimeWire ManagedThread, false to just make a regular Java thread
*/
public RRProcessingQueue(String name, boolean managed) {
// Call the ProcessingQueue constructor
super(name, managed);
}
/**
* Make a new ProcessingQueue which will make a new managed thread with the given name.
*
* @param name The name we'll give the thread we'll make to process the items
*/
public RRProcessingQueue(String name) {
// Call the ProcessingQueue constructor
super(name);
}
/**
* Give this RRProcessingQueue an object with a run() method for it to run.
* The RRProcessingQueue will have its thread call run() once, and then discard the object.
*
* @param runner The object with a run() method.
* @param queueId The name of the queue to put it in.
* An RRProcessingQueue is like a ProcessingQueue, but keeps multiple lists of Runnable objects.
* queueId is the name of the list to put the given runner in.
*/
public synchronized void invokeLater(Runnable runner, Object queueId) {
// Get the NamedQueue we have for the given queueId name
NamedQueue queue = (NamedQueue)queues.get(queueId);
if (queue == null) {
// We don't have a NamedQueue for queueId yet, make a new one
queue = new NamedQueue(new LinkedList(), queueId); // Use a new empty LinkedList and name it queueId
// Put the new NamedQueue we made in both queues and lists
queues.put(queueId, queue); // Add it to our queues Map
lists.enqueue(queue);
}
// Add the given Runnable object to the queue that has the matching queueId
queue.list.add(runner);
// Record we've got one more Runnable in this RRProcessingQueue
size++;
// Start our thread which will call the run() methods on Runnable objects, and then discard them
notifyAndStart();
}
/**
* Give this RRProcessingQueue an object with a run() method for it to run.
* The RRProcessingQueue will have its thread call run() once, and then discard the object.
*
* An RRProcessingQueue has many lists of Runnable objects.
* Each list is identified by an object that is its name.
* This invokeLater() method doesn't take a name object.
* It puts it in our list named by this object, sort of the default list.
*
* @param r The object with a run() method.
*/
public synchronized void invokeLater(Runnable r) {
// Call the other invokeLater() method
invokeLater(r, this); // Give it this instead of an object as the name, put r in the default list
}
/**
* Determine if this RRProcessingQueue still has more objects to run.
* Tells if there are any more in any of its lists.
*/
protected synchronized boolean moreTasks() {
// Return true if size, our count of Runnable objects in all of our lists, is more than 0
return size > 0;
}
/**
* Get the next Runnable object from among our lists for our thread to run and discard.
*
* This RRProcessingQueue might look like this:
*
* name1 name2 name3
* ----- ----- -----
* a b c
* d e f
*
* lists and queues both contain the 3 NamedQueue objects, with the names name1, name2, and name3.
* Each NamedQueue contains a list of Runnable objects, shown as the letters a through f.
*
* This next() method moves to the next list.
* For instance, if it was previously on name1, it moves to name2.
* It removes and returns the first runnable in that list.
* So, calling next() repeatedly will return the objects in the order a, b, c, d, e, and f.
* If a named list runs out of objects, next() removes it from the RRProcessingQueue entirely.
*
* @return The next object in this RRProcessingQueue its thread should call run.
* null if this whole RRProcessingQueue is empty.
*/
protected synchronized Runnable next() {
// Make a reference to the Runnable object from this RRProcessingQueue that we'll return
Runnable ret = null;
// Loop until we run out of NamedQueue lists
while (lists.size() > 0) {
// Point next at the list after the one we previously looked at
NamedQueue next = (NamedQueue)lists.next();
// Get a Runnable object from that list
ret = next.next(); // Removes the object from the list, and returns it
// We didn't get one because this list of Runnable objects is empty
if (ret == null || next.list.isEmpty()) {
// Remove the empty list from lists and queues
lists.removeAllOccurences(next);
queues.remove(next.name);
}
// If we got a Runnable object
if (ret != null) {
// next.next() above removed the object from the list, record that we have one less overall
size--;
// Return it
return ret;
}
}
// This whole RRProcessingQueue is empty
return null;
}
/**
* Find the total number of Runnable objects in all the named queues this RRProcessingQueue keeps.
*
* @return The number of objects we have
*/
public synchronized int size() {
// Return the size we counted
return size;
}
/**
* Clear all the contents of this RRProcessingQueue object.
*/
public synchronized void clear() {
// Clear the queues and lists, which both had references to the same NamedQueue objects
queues.clear();
lists.clear();
// Record that we don't have any Runnable objects in any of our lists
size = 0;
}
/**
* Remove a NamedQueue this RRProcessingQueue has.
* Removes the one with a given name.
*
* A RRProcessingQueue has keeps a list of NamedQueue objects.
* Each NamedQueue has a name.
* This method removes one.
*
* @param name The name of the NamedQueue to remove from this RRProcessingQueue
*/
public synchronized void clear(Object name) {
// Remove the NamedQueue we have with the given name
NamedQueue toRemove = (NamedQueue)queues.remove(name);
if (toRemove == null) return; // We don't have a NamedQueue with that name, so there is nothing for us to remove
// Remove the NamedQueue from lists also, it was referenced by both queues and lists
lists.removeAllOccurences(toRemove); // queues and lists both keep all of our NamedQueue objects
// We're losing all the Runnable objects in it, decrement size by that number
size -= toRemove.list.size(); // size is the total number of objects in all our NamedQueue objects
}
/** A NamedQueue keeps a list of Runnable objects, and has a name. */
private class NamedQueue {
/**
* A LinkedList for this NamedQueue to use.
* Code in the RRProcessingQueue class will put objects with run() methods in this list.
*/
final List list;
/** The Object that is this NamedQueue's name. */
final Object name;
/**
* Make a new NamedQueue object.
* Only RRProcessingQueue.invokeLater() does this.
*
* @param list A new empty LinkedList for this NamedQueue to use
* @param name An object to use as this new NamedQueue's name
*/
NamedQueue(List list, Object name) {
// Save the given objects
this.list = list;
this.name = name;
}
/**
* Get the next Runnable object in the list this NamedQueue keeps.
* Removes the object from the member variable list, and returns it.
*
* @return An object that implements the Runnable interface, and has a run() method.
* null if this NamedQueue's list is empty.
*/
Runnable next() {
// Return a Runnable object the RRProcessingQueue added to our list
return
list.isEmpty() ? // If our list is empty
null : // Return null, otherwise
(Runnable)list.remove(0); // Remove the first object in the list, and return it
}
}
}