/*************************************************************************** * Copyright (c) 2012-2013 VMware, Inc. All Rights Reserved. * 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 com.vmware.aurora.composition.concurrent; import java.util.AbstractQueue; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import com.vmware.aurora.util.AuAssert; /** * An unbounded {@linkplain BlockingQueue blocking queue} that maintains a separate * queue for each priority and supplies blocking retrieval operations. * While this queue is logically unbounded, attempted additions may fail due to * resource exhaustion (causing <tt>OutOfMemoryError</tt>). * This class does not permit <tt>null</tt> elements. * * The element removal methods, {@link #poll() poll}, {@link #poll(long, TimeUnit) poll(timeout)}, * and {@link #take() take} will check the {@link PriorityThreadPoolExecutor} which priority * has less active tasks than allowed maximal concurrent tasks, then first element of the queue * for that priority will be returned if that queue is not empty, * if it's empty, other queues will be checked, high priority is preferred. * * @author Xin Li (xinli) * */ class PriorityBlockingQueue<E extends PriorityFutureTask<?>> extends AbstractQueue<E> implements BlockingQueue<E> { private final ReentrantLock lock = new ReentrantLock(false); private final Condition notEmpty = lock.newCondition(); private final Queue<E>[] queues; private PriorityThreadPoolExecutor executor; /** * Creates a <tt>PriorityBlockingQueue</tt> with given number of all possible priorities. * @param queueCount Number of all possible priorities. */ @SuppressWarnings("unchecked") PriorityBlockingQueue(int queueCount) { queues = new Queue[queueCount]; for (int i = 0; i < queueCount; ++i) { queues[i] = new LinkedList<E>(); } } void setPriorityThreadPoolExecutor(final PriorityThreadPoolExecutor executor) { this.executor = executor; } @Override public E poll() { lock.lock(); try { int idx = executor.getNextPriorityToProcess(); E e = queues[idx].poll(); if (e == null) { idx = 0; for(Queue<E> q : queues) { e = q.poll(); if (e != null) { break; } ++idx; } } if (e != null) { executor.beforeExecute(idx); } return e; } finally { lock.unlock(); } } @Override public E peek() { lock.lock(); try { int idx = executor.getNextPriorityToProcess(); E e = queues[idx].peek(); if (e == null) { for (Queue<E> q : queues) { e = q.peek(); if (e != null) { break; } } } return e; } finally { lock.unlock(); } } @Override public boolean offer(E e) { int idx = e.getPriority().ordinal(); lock.lock(); try { boolean ret = queues[idx].offer(e); notEmpty.signal(); return ret; } finally { lock.unlock(); } } @Override public void put(E e) throws InterruptedException { offer(e); // no need to block } @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { return offer(e); // no need to block } @Override public E take() throws InterruptedException { lock.lockInterruptibly(); try { try { while (size() == 0) { notEmpty.await(); } } catch(InterruptedException e) { notEmpty.signal(); // propagate to non-interrupted thread throw e; } E e = poll(); AuAssert.check(e != null); return e; } finally { lock.unlock(); } } @Override public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); lock.lockInterruptibly(); try { for (;;) { E x = poll(); if (x != null) return x; if (nanos <= 0) return null; try { nanos = notEmpty.awaitNanos(nanos); } catch (InterruptedException ex) { notEmpty.signal(); // propagate to non-interrupted thread throw ex; } } } finally { lock.unlock(); } } @Override public int remainingCapacity() { return Integer.MAX_VALUE; } @Override public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); lock.lock(); try { int n = 0; E e; for (Queue<E> q : queues) { while ( (e = q.poll()) != null) { c.add(e); ++n; } } return n; } finally { lock.unlock(); } } @Override public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); if (maxElements <= 0) return 0; lock.lock(); try { int n = 0; E e; for (Queue<E> q : queues) { while (n < maxElements && (e = q.poll()) != null) { c.add(e); ++n; } } return n; } finally { lock.unlock(); } } /** * This method is not used by <tt>PriorityThreadPoolExecutor</tt> * @throws UnsupportedOperationException */ @Override public Iterator<E> iterator() { throw new UnsupportedOperationException(); } @Override public int size() { int size = 0; for (Queue<E> q : queues) { size += q.size(); } return size; } }