![JAR search and dependency download from the Maven repository](/logo.png)
com.embeddedunveiled.serial.util.RingArrayBlockingQueue Maven / Gradle / Ivy
Show all versions of scm Show documentation
/*
* Author : Rishi Gupta
*
* This file is part of 'serial communication manager' library.
* Copyright (C) <2014-2016>
*
* This 'serial communication manager' is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* The 'serial communication manager' is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with 'serial communication manager'. If not, see .
*/
package com.embeddedunveiled.serial.util;
import java.util.AbstractList;
import java.util.Collection;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* An expandable blocking/non-blocking FIFO queue backed by a ring buffer. It implements two lock
* concurrent queue algorithm.
*
* Inserting null elements is not allowed.
*
* Insertion methods : offer(), offer(timeout), add(), put()
* Removal methods : poll(), poll(timeout), take(), remove(), peek(), element()
* Inspection methods : peek(), size(), isEmpty(), clear(), remainingCapacity()
*
* @author Rishi Gupta
*/
public final class RingArrayBlockingQueue extends AbstractList implements BlockingQueue {
private final int DEFAULT_CAPACITY = 1024;
private final int DEFAULT_EXPANSION_BY = 1024;
private final int DEFAULT_MAX_CAPACITY = 4096;
private final Lock enqueueLock = new ReentrantLock();
private final Lock dequeueLock = new ReentrantLock();
private final AtomicInteger totalElementsInQueue = new AtomicInteger(0);
private final int capacity;
private final int maxCapacity;
private final int expandBy;
private E[] buffer;
// These 4 are accessed in happen-before relationship across threads.
private int tail = 0;
private int head = 0;
private int headUpdateStatus = 0;
private int tailUpdateStatus = 0;
// condition used with blocking await/signal
private final Condition waitForElementToBeAvailableCond;
/**
* Allocate and create queue with default initial capacity (DEFAULT_CAPACITY), default expansion
* factor (DEFAULT_EXPANSION_BY) and default maximum allowable size (DEFAULT_MAX_CAPACITY).
*
* @throws IllegalArgumentException if maxCapacity is zero or negative.
*/
@SuppressWarnings("unchecked")
public RingArrayBlockingQueue() {
capacity = DEFAULT_CAPACITY;
maxCapacity = DEFAULT_MAX_CAPACITY;
expandBy = DEFAULT_EXPANSION_BY;
buffer = (E[]) new Object[capacity];
waitForElementToBeAvailableCond = dequeueLock.newCondition();
}
/**
* Allocate and create queue with default initial capacity (DEFAULT_CAPACITY), default expansion
* factor (DEFAULT_EXPANSION_BY) and given maximum allowable size (maxCapacity).
*
* @param maxCapacity maximum size of queue.
* @throws IllegalArgumentException if maxCapacity is zero or negative.
*/
@SuppressWarnings("unchecked")
public RingArrayBlockingQueue(int maxCapacity) {
if (maxCapacity <= 0) {
throw new IllegalArgumentException("Argument maxCapacity can not be negative or zero !");
}
capacity = DEFAULT_CAPACITY;
this.maxCapacity = maxCapacity;
expandBy = DEFAULT_EXPANSION_BY;
buffer = (E[]) new Object[capacity];
waitForElementToBeAvailableCond = dequeueLock.newCondition();
}
/**
* Allocate and create queue according to the given initial capacity, expansion factor and
* maximum allowable size.
*
* @param capacity initial size of queue.
* @param expandBy number that should be added to current size of queue to expand it.
* @param maxCapacity maximum size of queue.
* @throws IllegalArgumentException if capacity/maxCapacity/expandBy is zero or negative.
*/
@SuppressWarnings("unchecked")
public RingArrayBlockingQueue(int capacity, int expandBy, int maxCapacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("capacity can not be negative or zero !");
}
if (maxCapacity <= 0) {
throw new IllegalArgumentException("Argument maxCapacity can not be negative or zero !");
}
if (expandBy <= 0) {
throw new IllegalArgumentException("Argument expandBy can not be negative or zero !");
}
this.capacity = capacity;
this.maxCapacity = maxCapacity;
this.expandBy = expandBy;
buffer = (E[]) new Object[capacity];
// when take() blocks, another thread will call signal() on condition associated with
// dequeueLock i.e. waitForElementToBeAvailableCond. Similar approach applies for
// enque as well.
waitForElementToBeAvailableCond = dequeueLock.newCondition();
}
/*
* Expand the array by expandBy value. The situation to expand the array arises only when rate of
* insertion was higher than rate of removal. If the tail rolled over, this method will re-arrange
* all elements as it would have been in big linear 1-D array. After expansion next location is
* calculated as :
* next location = number of element at the time this method was called + 1;
*/
@SuppressWarnings("unchecked")
private boolean expandQueue() {
int x = 0;
int y = 0;
int z = 0;
// If we have reached maximum allowable size, consumer must read the elements from queue to make
// room for new elements to be inserted into queue. There is no way out, so return false;
if(buffer.length == maxCapacity) {
return false;
}
enqueueLock.lock();
dequeueLock.lock();
// Size of new queue can never be greater than maxCapacity.
int newLength = buffer.length + expandBy;
if(newLength >= maxCapacity) {
newLength = maxCapacity;
}
try {
E[] tmp = (E[]) new Object[newLength];
if(tail > head) {
// tail kept increasing linearly and finally the queue was full.
for(x = 0; x < buffer.length; x++) {
tmp[x] = buffer[x];
}
}else {
// tail kept increasing and rolled over, but than found that queue is full.
y = head;
for(x = 0; x < (buffer.length - head); x++) {
tmp[x] = buffer[y];
y++;
}
for(z = 0; z <= tail; z++) {
tmp[x] = buffer[z];
x++;
}
}
// new bigger queue
buffer = tmp;
} finally {
dequeueLock.unlock();
enqueueLock.unlock();
}
return true;
}
/*
* Inserts the specified element into this queue, waiting up to the specified wait time if necessary for
* space to become available.
*
* Called by put(), offer() and add().
*
* @param e the element to add.
* @param timeout -1 for do not wait, -2 for wait indefinitely, for rest values wait as per given timeout value.
* @param unit a TimeUnit determining how to interpret the timeout parameter, null if timeout is -1 or -2.
* @return true if element gets inserted into the queue, false otherwise.
* @throws ClassCastException if the class of the specified element prevents it from being added to this queue.
* @throws NullPointerException if the specified element is null.
* @throws IllegalArgumentException if some property of the specified element prevents it from being added to
* this queue.
*/
private boolean insert(E e, long timeout, TimeUnit unit) throws InterruptedException {
boolean elementAdded = false;
int totalElementBeforeInsertion = 0;
if (e == null) {
throw new NullPointerException("Null elements may not be inserted in this queue !");
}
enqueueLock.lock();
try {
totalElementBeforeInsertion = totalElementsInQueue.get();
if(buffer.length >= maxCapacity) {
/* buffer can not be expanded further. */
if(totalElementBeforeInsertion >= maxCapacity) {
// Queue is already full and can not be expanded.
if(timeout == -1) {
return false;
}
else if(timeout == -2) {
// handle spurious signal, wait until queue really has space.
while(totalElementsInQueue.get() >= buffer.length) {
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
throw ie;
}
}
// check if updating tail before insertion is needed or not.
if(tailUpdateStatus == -1) {
tail = 0;
tailUpdateStatus = 0;
}else if(tailUpdateStatus == -2) {
tail++;
tailUpdateStatus = 0;
}else {
}
buffer[tail] = e;
totalElementsInQueue.incrementAndGet();
elementAdded = true;
// update tail an associated status as required.
if((totalElementBeforeInsertion + 1) >= buffer.length) {
if(tail == (buffer.length - 1)) {
tailUpdateStatus = -1;
}else {
tailUpdateStatus = -2;
}
}else {
if(tail == (buffer.length - 1)) {
tail = 0;
tailUpdateStatus = 0;
}else {
tail++;
tailUpdateStatus = 0;
}
}
}
else {
try {
Thread.sleep(unit.toMillis(timeout));
if (totalElementsInQueue.get() >= buffer.length) {
// timed out while waiting.
return false;
}else {
if(tailUpdateStatus == -1) {
tail = 0;
tailUpdateStatus = 0;
}else if(tailUpdateStatus == -2) {
tail++;
tailUpdateStatus = 0;
}else {
}
buffer[tail] = e;
totalElementsInQueue.incrementAndGet();
elementAdded = true;
if((totalElementBeforeInsertion + 1) >= buffer.length) {
if(tail == (buffer.length - 1)) {
tailUpdateStatus = -1;
}else {
tailUpdateStatus = -2;
}
}else {
if(tail == (buffer.length - 1)) {
tail = 0;
tailUpdateStatus = 0;
}else {
tail++;
tailUpdateStatus = 0;
}
}
}
} catch (InterruptedException ie) {
throw ie;
}
}
}else {
if(tailUpdateStatus == -1) {
tail = 0;
tailUpdateStatus = 0;
}else if(tailUpdateStatus == -2) {
tail++;
tailUpdateStatus = 0;
}else {
}
buffer[tail] = e;
totalElementsInQueue.incrementAndGet();
elementAdded = true;
if((totalElementBeforeInsertion + 1) >= buffer.length) {
if(tail == (buffer.length - 1)) {
tailUpdateStatus = -1;
}else {
tailUpdateStatus = -2;
}
}else {
if(tail == (buffer.length - 1)) {
tail = 0;
tailUpdateStatus = 0;
}else {
tail++;
tailUpdateStatus = 0;
}
}
}
}else {
/* buffer can be expanded further if required. */
if(totalElementBeforeInsertion >= buffer.length) {
// queue is already full, expansion is required.
int tmp = buffer.length;
dequeueLock.lock();
try {
if(expandQueue() == true) {
buffer[tmp] = e;
tail = tmp;
totalElementsInQueue.incrementAndGet();
elementAdded = true;
if((totalElementBeforeInsertion + 1) >= buffer.length) {
if(tail == (buffer.length - 1)) {
tailUpdateStatus = -1;
}
}else {
if(tail == (buffer.length - 1)) {
tail = 0;
tailUpdateStatus = 0;
}else {
tail++;
tailUpdateStatus = 0;
}
}
}else {
return false;
}
} finally {
dequeueLock.unlock();
}
}else {
if(tailUpdateStatus == -1) {
tail = 0;
tailUpdateStatus = 0;
}else if(tailUpdateStatus == -2) {
tail++;
tailUpdateStatus = 0;
}else {
}
buffer[tail] = e;
totalElementsInQueue.incrementAndGet();
elementAdded = true;
if((totalElementBeforeInsertion + 1) >= buffer.length) {
if(tail == (buffer.length - 1)) {
tailUpdateStatus = -1;
}else {
tailUpdateStatus = -2;
}
}else {
if(tail == (buffer.length - 1)) {
tail = 0;
tailUpdateStatus = 0;
}else {
tail++;
tailUpdateStatus = 0;
}
}
}
}
// If the element is inserted into queue, acquire the lock associated with condition and signal others.
if(elementAdded == true) {
dequeueLock.lock();
try {
waitForElementToBeAvailableCond.signalAll();
} finally {
dequeueLock.unlock();
}
}
} finally {
enqueueLock.unlock();
}
return true;
}
/*
* Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an
* element to become available.
*
* poll() and take() calls this method.
*
* @param timeout -1 for do not wait, -2 for wait indefinitely, for rest values wait as per given timeout value.
* @param unit a TimeUnit determining how to interpret the timeout parameter.
* @return the head of this queue, or null if the specified waiting time elapses before an element is available.
* @throws InterruptedException if interrupted while waiting.
*/
private E removeElement(long timeout, TimeUnit unit) throws InterruptedException {
E element = null;
int totalElementBeforeRemoval = 0;
dequeueLock.lockInterruptibly();
try {
totalElementBeforeRemoval = totalElementsInQueue.get();
if(totalElementBeforeRemoval == 0) {
// Queue is absolutely empty.
if(timeout == -1) {
return null;
}
else if(timeout == -2) {
// handle spurious signal, wait until queue really has at-least one element.
while(totalElementsInQueue.get() == 0) {
try {
waitForElementToBeAvailableCond.await();
}catch (InterruptedException ie) {
waitForElementToBeAvailableCond.signal(); // propagate to non-interrupted thread
throw ie;
}
}
}
else {
long nanosTimeout = unit.toNanos(timeout);
while(totalElementsInQueue.get() == 0) {
if(nanosTimeout <= 0) {
return null;
}
try {
// nanosTimeout is 0 or negative if time elapsed.
if (nanosTimeout > 0) {
nanosTimeout = waitForElementToBeAvailableCond.awaitNanos(nanosTimeout);
}
}catch (InterruptedException ie) {
waitForElementToBeAvailableCond.signal(); // propagate to non-interrupted thread
throw ie;
}
}
}
}else {
// Queue has at-least 1 element ready to be removed.
}
// check if updating head before removal is needed or not.
if(headUpdateStatus == -1) {
head = 0;
headUpdateStatus = 0;
}else if(headUpdateStatus == -2) {
head++;
headUpdateStatus = 0;
}else {
}
element = buffer[head];
buffer[head] = null;
totalElementsInQueue.decrementAndGet();
if((totalElementBeforeRemoval - 1) <= 0) {
if(head == (buffer.length - 1)) {
headUpdateStatus = -1;
}else {
headUpdateStatus = -2;
}
}else {
if(head == (buffer.length - 1)) {
head = 0;
headUpdateStatus = 0;
}else {
head++;
headUpdateStatus = 0;
}
}
if((totalElementBeforeRemoval - 1) > 0) {
waitForElementToBeAvailableCond.signalAll();
}
}finally {
dequeueLock.unlock();
}
return element;
}
/**
* Retrieves and removes the head of this queue, or returns null if this queue is empty.
*
* @return the head of this queue, or null if this queue is empty.
*/
@Override
public E poll() {
E element = null;
try {
element = removeElement(-1, null);
} catch (InterruptedException e) {
}
return element;
}
/**
* Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an
* element to become available.
*
* @param timeout how long to wait before giving up, in units of unit.
* @param unit a TimeUnit determining how to interpret the timeout parameter.
* @return the head of this queue, or null if the specified waiting time elapses before an element is available.
* @throws InterruptedException if interrupted while waiting.
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
return removeElement(timeout, unit);
}
/**
* Retrieves and removes the head of this queue, waiting if necessary until an element becomes available.
*
* @return the head of this queue.
* @throws InterruptedException if interrupted while waiting.
*/
@Override
public E take() throws InterruptedException {
return removeElement(-2, null);
}
/**
* Retrieves and removes the head of this queue. If the queue is empty, NoSuchElementException
* is thrown.
*
* @return the head of this queue.
* @throws NoSuchElementException if queue is empty.
*/
@Override
public E remove() {
E element = null;
try {
element = removeElement(-1, null);
} catch (InterruptedException e) {
}
if (element == null) {
throw new NoSuchElementException();
}
return element;
}
/**
* Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
*
* @return the head of this queue, or null if this queue is empty.
*/
@Override
public E peek() {
E element = null;
dequeueLock.lock();
try {
if(totalElementsInQueue.get() > 0) {
element = buffer[head];
}
}finally {
dequeueLock.unlock();
}
return element;
}
/**
* Retrieves, but does not remove, the head of this queue. If the queue is empty, NoSuchElementException
* is thrown.
*
* @return the head of this queue.
* @throws NoSuchElementException if this queue is empty.
*/
@Override
public E element() {
E element = peek();
if (element == null) {
throw new NoSuchElementException();
}
return element;
}
/**
* Use take() or poll() to get an element.
*
* @throws UnsupportedOperationException use other methods to retrieve elements from queue.
*/
@Override
public E get(int index) {
throw new UnsupportedOperationException("Use take() or poll() to get an element !");
}
/**
* Inserts the specified element into this queue if it is possible to do so immediately without violating
* capacity restrictions, expanding the queue if required and possible, returning true if elements gets inserted
* into queue, false otherwise.
*
* @param e the element to add.
* @return true if the element was added to this queue otherwise false.
* @throws ClassCastException if the class of the specified element prevents it from being added to this queue.
* @throws NullPointerException if the specified element is null.
* @throws IllegalArgumentException if some property of this element prevents it from being added to this queue.
*/
@Override
public boolean offer(E e) {
boolean result = false;
try {
result = insert(e, -1, null);
} catch (Exception exp) {
if(exp instanceof InterruptedException) {
// This will not happen as insert() will not wait when this version of offer() is invoked.
return false;
}else if(exp instanceof NullPointerException) {
throw new NullPointerException();
}else if(exp instanceof ClassCastException) {
throw (ClassCastException) exp;
}else if(exp instanceof IllegalArgumentException) {
throw (IllegalArgumentException) exp;
}else {
return false;
}
}
return result;
}
/**
* Inserts the specified element into this queue, waiting up to the specified wait time if necessary for
* space to become available.
*
* @param e the element to add.
* @param timeout how long to wait before giving up, in units of unit.
* @param unit a TimeUnit determining how to interpret the timeout parameter.
* @return true if successful, or false if the specified waiting time elapses before space is available.
* @throws ClassCastException if the class of the specified element prevents it from being added to this queue.
* @throws NullPointerException if the specified element is null.
* @throws IllegalArgumentException if some property of this element prevents it from being added to this queue.
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
return insert(e, timeout, unit);
}
/**
* Inserts the specified element into this queue if it is possible to do so immediately without violating
* capacity restrictions, expanding the queue if required and possible, returning true if elements gets inserted
* into the queue otherwise throws IllegalStateException exception.
*
* @param e the element to add.
* @return true if element get added into queue.
* @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions.
* @throws ClassCastException if the class of the specified element prevents it from being added to this queue.
* @throws NullPointerException if the specified element is null and this queue does not permit null elements.
* @throws IllegalArgumentException if some property of this element prevents it from being added to this queue.
*/
@Override
public boolean add(E e) {
boolean result = false;
try {
result = insert(e, -1, null);
} catch (Exception exp) {
exp.printStackTrace();
if(exp instanceof InterruptedException) {
throw new IllegalStateException();
}else if(exp instanceof NullPointerException) {
throw new NullPointerException();
}else if(exp instanceof ClassCastException) {
throw (ClassCastException) exp;
}else if(exp instanceof IllegalArgumentException) {
throw (IllegalArgumentException) exp;
}else {
throw new IllegalStateException();
}
}
return result;
}
/**
* Inserts the specified element into this queue, waiting if necessary for space to become available.
*
* @param e the element to add.
* @throws InterruptedException if interrupted while waiting.
* @throws ClassCastException if the class of the specified element prevents it from being added to this queue.
* @throws NullPointerException if the specified element is null.
* @throws IllegalArgumentException if some property of this element prevents it from being added to this queue.
*/
@Override
public void put(E e) throws InterruptedException {
try {
insert(e, -2, null);
} catch (Exception exp) {
if(exp instanceof InterruptedException) {
throw new IllegalStateException();
}else if(exp instanceof NullPointerException) {
throw new NullPointerException();
}else if(exp instanceof ClassCastException) {
throw (ClassCastException) exp;
}else if(exp instanceof IllegalArgumentException) {
throw (IllegalArgumentException) exp;
}else {
throw new IllegalStateException();
}
}
}
/**
* Returns the number of additional elements that this queue can ideally (in the absence of memory or resource
* constraints) accept without blocking. Note that we cannot always tell if an attempt to insert an element will
* succeed by inspecting remainingCapacity because it may be the case that another thread is about to insert or
* remove an element.
*
* @return the remaining capacity of this queue.
*/
@Override
public int remainingCapacity() {
int remainingSize = 0;
enqueueLock.lock();
dequeueLock.lock();
try {
remainingSize = buffer.length - totalElementsInQueue.get();
} finally {
dequeueLock.unlock();
enqueueLock.unlock();
}
return remainingSize;
}
/**
* Returns the number of elements in this collection. If this collection contains more than Integer.MAX_VALUE
* elements, returns Integer.MAX_VALUE.
*
* @return the number of elements in this collection.
*/
@Override
public int size() {
return totalElementsInQueue.get();
}
/**
* Removes all available elements from this queue and adds them to the given collection. This operation may
* be more efficient than repeatedly polling this queue. A failure encountered while attempting to add elements
* to collection c may result in elements being in neither, either or both collections when the associated
* exception is thrown.
*
* Attempts to drain a queue to itself result in IllegalArgumentException. Further, the behavior of this
* operation is undefined if the specified collection is modified while the operation is in progress.
*
* This batched reading style may be useful in case where rate of insertion in queue is faster than rate at
* which elements are removed from queue.
*
* @param c the collection to transfer elements into.
* @return the number of elements transferred.
* @throws UnsupportedOperationException if addition of elements is not supported by the specified collection.
* @throws ClassCastException if the class of an element of this queue prevents it from being added to the
* specified collection.
* @throws NullPointerException if the specified collection is null.
* @throws IllegalArgumentException if the specified collection is this queue, or some property of an element
* of this queue prevents it from being added to the specified collection.
*/
@Override
public int drainTo(Collection super E> c) {
int x = 0;
int y = 0;
int numElementsInQueueRightNow = 0;
if(c == null) {
throw new NullPointerException();
}
if(c == this) {
throw new IllegalArgumentException();
}
// acquire both the locks to take snapshot of queue to return.
dequeueLock.lock();
enqueueLock.lock();
try {
numElementsInQueueRightNow = totalElementsInQueue.get();
if(numElementsInQueueRightNow != 0) {
if(headUpdateStatus == -1) {
x = 0;
}else if(headUpdateStatus == -2) {
x = ++head;
}else {
x = head;
}
while(y < numElementsInQueueRightNow) {
c.add(buffer[x]);
buffer[x] = null;
x++;
if(x == buffer.length) {
x = 0;
}
y++;
}
}
totalElementsInQueue.set(0);
head = 0;
tail = 0;
headUpdateStatus = 0;
tailUpdateStatus = 0;
} finally {
enqueueLock.unlock();
dequeueLock.unlock();
}
return numElementsInQueueRightNow;
}
/**
* Removes at most the given number of available elements from this queue and adds them to the given
* collection. A failure encountered while attempting to add elements to collection c may result in elements
* being in neither, either or both collections when the associated exception is thrown.
*
* Attempts to drain a queue to itself result in IllegalArgumentException. Further, the behavior of this
* operation is undefined if the specified collection is modified while the operation is in progress.
*
* This batched reading style may be useful in case where rate of insertion in queue is faster than rate at
* which elements are removed from queue.
*
* @param c the collection to transfer elements into.
* @param maxElements the maximum number of elements to transfer.
* @return the number of elements transferred.
* @throws UnsupportedOperationException if addition of elements is not supported by the specified collection.
* @throws ClassCastException if the class of an element of this queue prevents it from being added to the
* specified collection.
* @throws NullPointerException if the specified collection is null.
* @throws IllegalArgumentException if the specified collection is this queue, or some property of an element
* of this queue prevents it from being added to the specified collection.
*/
@Override
public int drainTo(Collection super E> c, int maxElements) {
int x = 0;
int y = 0;
int numOfElementsToDrain = 0;
int numElementsInQueueRightNow = 0;
if(c == null) {
throw new NullPointerException();
}
if(c == this) {
throw new IllegalArgumentException();
}
// acquire both the locks to take snapshot of queue to return.
dequeueLock.lock();
enqueueLock.lock();
try {
numElementsInQueueRightNow = totalElementsInQueue.get();
if(numElementsInQueueRightNow > maxElements) {
numOfElementsToDrain = maxElements;
}else {
numOfElementsToDrain = numElementsInQueueRightNow;
}
if(numElementsInQueueRightNow > 0) {
if(headUpdateStatus == -1) {
x = 0;
}else if(headUpdateStatus == -2) {
x = ++head;
}else {
x = head;
}
while(y < numOfElementsToDrain) {
c.add(buffer[x]);
buffer[x] = null;
x++;
if(x == buffer.length) {
x = 0;
}
y++;
}
}
totalElementsInQueue.set(0);
head = 0;
tail = 0;
headUpdateStatus = 0;
tailUpdateStatus = 0;
} finally {
enqueueLock.unlock();
dequeueLock.unlock();
}
return numOfElementsToDrain;
}
/**
* Atomically removes all of the elements from this queue. The queue will be empty after this
* call returns.
*/
@Override
public void clear() {
int x = 0;
int y = 0;
int numElementsInQueueRightNow = 0;
// acquire both the locks to take snapshot of queue to return.
dequeueLock.lock();
enqueueLock.lock();
try {
numElementsInQueueRightNow = totalElementsInQueue.get();
if(numElementsInQueueRightNow > 0) {
if(headUpdateStatus == -1) {
x = 0;
}else if(headUpdateStatus == -2) {
x = ++head;
}else {
x = head;
}
while(y < numElementsInQueueRightNow) {
buffer[x] = null;
x++;
if(x == buffer.length) {
x = 0;
}
y++;
}
}
totalElementsInQueue.set(0);
head = 0;
tail = 0;
headUpdateStatus = 0;
tailUpdateStatus = 0;
} finally {
enqueueLock.unlock();
dequeueLock.unlock();
}
}
/**
* Returns true if queue is not empty otherwise false.
*
* @return true if queue is not empty otherwise false.
*/
public boolean isEmpty() {
if(totalElementsInQueue.get() > 0) {
return false;
}
return true;
}
/**
* Provide an array containing all the elements in the same as they were inserted into this
* queue. The drainTo() method removes elements from the queue while this method does not remove
* elements from queue.
*
* @return an array containing all the elements in this queue.
*/
public Object[] toArray() {
int x = 0;
int y = 0;
int z = 0;
int numElementsInQueueRightNow = 0;
Object[] r = null;
dequeueLock.lock();
enqueueLock.lock();
try {
numElementsInQueueRightNow = totalElementsInQueue.get();
if(numElementsInQueueRightNow > 0) {
r = new Object[numElementsInQueueRightNow];
if(headUpdateStatus == -1) {
x = 0;
}else if(headUpdateStatus == -2) {
x = ++head;
}else {
x = head;
}
while(y < numElementsInQueueRightNow) {
r[z] = buffer[x];
x++;
if(x == buffer.length) {
x = 0;
}
z++;
y++;
}
}else {
return new Object[0];
}
} finally {
enqueueLock.unlock();
dequeueLock.unlock();
}
return r;
}
}