
com.github.basking2.sdsai.sandbox.Queue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdsai-sandbox Show documentation
Show all versions of sdsai-sandbox Show documentation
Simple datastructures and algorithms.
The newest version!
/* $Id: Queue.java 770 2008-09-03 21:39:19Z sam $ */
package com.github.basking2.sdsai.sandbox;
public class Queue
{
/**
* This is a Queue Element.
*/
protected class QE
{
public QE next;
public F data;
public QE(F d, QE n){ data = d; next = n; }
}
private QE head = null;
private QE tail = null;
/**
* If a queue is closed it will never block but perpetually returns
* NULL. Future enhancements to this be to change that to
* be some default END-OF-QUEUE object.
*/
private boolean closed = false;
protected int size = 0;
/**
* We want no other thing to be able to lock/unlock/notify
* this queue. Thus, we hide the lock in here.
*/
private Object lock;
/**
* if greater than 0 then this Queue will not hold more than limit
* elements.
*/
protected int limit = 0;
/**
* If there is a limit on the queue length, block the thread that
* is attempting to add more data. This also causes dequeue to
* notifyAll on this object.
*/
protected boolean block = false;
/**
* If limit is greater than 0 do we dropTail (drop the oldest element)
* or ignore the added element.
*/
protected boolean dropHead = false;
public Queue()
{
lock = new Object();
}
public void setDropHead(boolean b)
{
dropHead = b;
}
public boolean getDropHead()
{
return dropHead;
}
/**
* If this is set to true, the the queue will block when the limit
* is exceeded on an enqueue. This takes precedence over drop head or
* drop tail methods.
*/
public void setBlock(boolean b)
{
block = b;
}
public boolean getBlock()
{
return block;
}
public int getLimit()
{
return limit;
}
public boolean willBlock()
{
return (size >= limit && limit <= 0);
}
/**
* Set a limit on the length of this Queue. Zero is no limit.
*/
public void setLimit(int l)
{
limit = l;
}
public E enqueue (E o)
{
return enqueue(o, 0);
}
/**
* This method will attempt to enqueue an object according to the
* rules of this queue. If an element is dropped (or in the case
* of "drop head", and element is not added) it is returned.
* If the object is added, then null is returned.
*
* @param o object to enqueue.
* @param timeout number of milliseconds to wait for an object if this queue causes a block.
* @return If the element o could not be added due to a timeout, then it is returned.
* If because of a drop tail policy in the queue, the dropped element is returned. If
* o is added successfully, null is returned.
*
*/
public E enqueue(E o, long timeout)
{
E droppedElement = null;
synchronized(lock) {
// If there is no limit, just add it.
if(limit <= 0){
forceEnqueue(o);
// If we haven't met the limit, add it.
} else if(size < limit) {
forceEnqueue(o);
// We prefer to block over dropTail.
} else if(block) {
synchronized(lock){
if ( timeout > 0 ) {
long startTime = System.currentTimeMillis();
long stopTime = 0;
long currentTimeout = timeout;
do {
try {
lock.wait(currentTimeout);
stopTime = System.currentTimeMillis();
} catch(InterruptedException e) {
// upon spurious wake-up, adjust our timeout.
currentTimeout = timeout - ( System.currentTimeMillis() - startTime );
// Just in the wild case that we get a negative currentTimeout...
// ... we setup to exit the loops as if we have exceeded the timeout...
// ... ... (which we did)... (cause it's negative)...
if ( currentTimeout > 0 ) {
stopTime = System.currentTimeMillis();
}
}
} while ( willBlock() && stopTime <= 0 );
if ( stopTime - startTime <= timeout )
forceEnqueue(o);
else
droppedElement = o;
// Else, the timeout is zero.
} else {
// The do-while loop accounts for the occasional accidental wakeup.
do {
try
{ lock.wait(); }
catch(InterruptedException e)
{ /* No recovery action. */ }
} while(willBlock());
}
} // end synchronized.
forceEnqueue(o);
/* If we have met the limit and our solution is dropTail. */
} else if(dropHead){
droppedElement = dequeue();
forceEnqueue(o);
/* If we cannot fit in the new element and we can't drop the head
* we drop the tail. */
} else {
droppedElement = o;
}
}
return droppedElement;
}
/**
* Enqueue the object without checking any limits to this Queue's length.
* This method is called by enqueue when it concludes that we can add
* an object to this object.
*/
public void forceEnqueue(E o)
{
synchronized(lock) {
if(head==null){
head = new QE(o, null);
tail = head;
} else {
tail.next = new QE(o, null);
tail = tail.next;
}
size++;
lock.notifyAll();
}
}
public E dequeue()
{
return dequeue(0);
}
/**
* Block until there is data to return if this
* is a blocking queue. Returns the default end of queue object
* (null by default) if the timeout
* expires. A timeout of zero indicates that this should
* block until the queue is non-empty.
* @return
*/
public E dequeue(long timeout)
{
E o = null;
synchronized(lock)
{
if ( closed )
return getEndOfQueueObject();
if ( empty() )
{
if ( block )
{
if ( timeout > 0 )
{
// Block with a timeout.
long startTime = System.currentTimeMillis();
long currentTimeout = timeout;
do {
try {
lock.wait(currentTimeout);
currentTimeout = timeout - ( System.currentTimeMillis() - startTime );
} catch(InterruptedException e) {
// do nothing.
}
} while(empty() && currentTimeout > 0 && !closed);
// If still empty, quit.
if ( empty() || closed )
return getEndOfQueueObject();
} else {
// Block with no timeout.
while(empty() && !closed) {
try {
lock.wait();
} catch(InterruptedException e) {
// do nothing.
}
}
if ( closed )
return getEndOfQueueObject();
}
} else {
// non blocking with no data. Very easy. :)
return getEndOfQueueObject();
}
}
// Not closed and not empty. Just take an element.
o = head.data;
if(size==1){
head = null;
tail = null;
} else {
head = head.next;
}
size--;
lock.notifyAll();
}
return o;
}
/**
* Safely empty the queue and notify all thread waiting on this.
* This is useful for shutting down a queue. Set it to non-blocking, then
* flush it.
*/
public void flush()
{
synchronized(lock)
{
if ( size > 0 ) {
size = 0;
head = null;
tail = null;
}
lock.notifyAll();
}
}
public boolean empty(){ return size == 0; }
public E peek(){ return head==null ? null : head.data; }
public int size(){ return size; }
public E getEndOfQueueObject()
{
return null;
}
public void setClosed(boolean closed)
{
synchronized(lock){
this.closed = closed;
lock.notifyAll();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy