/* * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.ros.concurrent; import java.util.Iterator; import java.util.NoSuchElementException; /** * A deque that removes head or tail elements when the number of elements * exceeds the limit and blocks on {@link #takeFirst()} and {@link #takeLast()} * when there are no elements available. * * @author damonkohler@google.com (Damon Kohler) */ public class CircularBlockingDeque<T> implements Iterable<T> { private final T[] deque; private final Object mutex; /** * The maximum number of entries in the queue. */ private final int limit; /** * Points to the next entry that will be returned by {@link #takeFirst()} * unless {@link #isEmpty()}. */ private int start; /** * The number of entries in the queue. */ private int length; /** * @param capacity * the maximum number of elements allowed in the queue */ @SuppressWarnings("unchecked") public CircularBlockingDeque(int capacity) { deque = (T[]) new Object[capacity]; mutex = new Object(); limit = capacity; start = 0; length = 0; } /** * Adds the specified entry to the tail of the queue, overwriting older * entries if necessary. * * @param entry * the entry to add * @return {@code true} */ public boolean addLast(T entry) { synchronized (mutex) { deque[(start + length) % limit] = entry; if (length == limit) { start = (start + 1) % limit; } else { length++; } mutex.notify(); } return true; } /** * Adds the specified entry to the tail of the queue, overwriting older * entries if necessary. * * @param entry * the entry to add * @return {@code true} */ public boolean addFirst(T entry) { synchronized (mutex) { if (start - 1 < 0) { start = limit - 1; } else { start--; } deque[start] = entry; if (length < limit) { length++; } mutex.notify(); } return true; } /** * Retrieves the head of the queue, blocking if necessary until an entry is * available. * * @return the head of the queue * @throws InterruptedException */ public T takeFirst() throws InterruptedException { T entry; synchronized (mutex) { while (true) { if (length > 0) { entry = deque[start]; start = (start + 1) % limit; length--; break; } mutex.wait(); } } return entry; } /** * Retrieves the head of the queue. * * <p> * There is no waiting. * * @return the head of the queue, or {@code null} if nothing is in the queue. * * @throws InterruptedException */ public T poll() { T entry = null; synchronized (mutex) { if (length > 0) { entry = deque[start]; start = (start + 1) % limit; length--; } } return entry; } /** * Retrieves, but does not remove, the head of this queue, returning * {@code null} if this queue is empty. * * @return the head of this queue, or {@code null} if this queue is empty */ public T peekFirst() { synchronized (mutex) { if (length > 0) { return deque[start]; } return null; } } /** * Retrieves the tail of the queue, blocking if necessary until an entry is * available. * * @return the tail of the queue * @throws InterruptedException */ public T takeLast() throws InterruptedException { T entry; synchronized (mutex) { while (true) { if (length > 0) { entry = deque[(start + length - 1) % limit]; length--; break; } mutex.wait(); } } return entry; } /** * Retrieves, but does not remove, the tail of this queue, returning * {@code null} if this queue is empty. * * @return the tail of this queue, or {@code null} if this queue is empty */ public T peekLast() { synchronized (mutex) { if (length > 0) { return deque[(start + length - 1) % limit]; } return null; } } public boolean isEmpty() { return length == 0; } /** * Returns an iterator over the queue. * <p> * Note that this is not thread-safe and that {@link Iterator#remove()} is * unsupported. * * @see java.lang.Iterable#iterator() */ @Override public Iterator<T> iterator() { return new Iterator<T>() { int offset = 0; @Override public boolean hasNext() { return offset < length; } @Override public T next() { if (offset == length) { throw new NoSuchElementException(); } T entry = deque[(start + offset) % limit]; offset++; return entry; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }