/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.activemq.artemis.utils.collections; import java.lang.reflect.Array; import java.util.NoSuchElementException; /** * A priority linked list implementation * <p> * It implements this by maintaining an individual LinkedBlockingDeque for each priority level. */ public class PriorityLinkedListImpl<T> implements PriorityLinkedList<T> { protected LinkedListImpl<T>[] levels; private int size; private int lastReset; private int highestPriority = -1; private int lastPriority = -1; public PriorityLinkedListImpl(final int priorities) { levels = (LinkedListImpl<T>[]) Array.newInstance(LinkedListImpl.class, priorities); for (int i = 0; i < priorities; i++) { levels[i] = new LinkedListImpl<>(); } } private void checkHighest(final int priority) { if (lastPriority != priority || priority > highestPriority) { lastPriority = priority; if (lastReset == Integer.MAX_VALUE) { lastReset = 0; } else { lastReset++; } } if (priority > highestPriority) { highestPriority = priority; } } @Override public void addHead(final T t, final int priority) { checkHighest(priority); levels[priority].addHead(t); size++; } @Override public void addTail(final T t, final int priority) { checkHighest(priority); levels[priority].addTail(t); size++; } @Override public T poll() { T t = null; // We are just using a simple prioritization algorithm: // Highest priority refs always get returned first. // This could cause starvation of lower priority refs. // TODO - A better prioritization algorithm for (int i = highestPriority; i >= 0; i--) { LinkedListImpl<T> ll = levels[i]; if (ll.size() != 0) { t = ll.poll(); if (t != null) { size--; if (ll.size() == 0) { if (highestPriority == i) { highestPriority--; } } } break; } } return t; } @Override public void clear() { for (LinkedListImpl<T> list : levels) { list.clear(); } size = 0; } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public LinkedListIterator<T> iterator() { return new PriorityLinkedListIterator(); } private class PriorityLinkedListIterator implements LinkedListIterator<T> { private int index; private final LinkedListIterator<T>[] cachedIters = new LinkedListIterator[levels.length]; private LinkedListIterator<T> lastIter; private int resetCount = lastReset; volatile boolean closed = false; PriorityLinkedListIterator() { index = levels.length - 1; } @Override protected void finalize() { close(); } @Override public void repeat() { if (lastIter == null) { throw new NoSuchElementException(); } lastIter.repeat(); } @Override public void close() { if (!closed) { closed = true; lastIter = null; for (LinkedListIterator<T> iter : cachedIters) { if (iter != null) { iter.close(); } } } } private void checkReset() { if (lastReset != resetCount) { index = highestPriority; resetCount = lastReset; } } @Override public boolean hasNext() { checkReset(); while (index >= 0) { lastIter = cachedIters[index]; if (lastIter == null) { lastIter = cachedIters[index] = levels[index].iterator(); } boolean b = lastIter.hasNext(); if (b) { return true; } index--; if (index < 0) { index = levels.length - 1; break; } } return false; } @Override public T next() { if (lastIter == null) { throw new NoSuchElementException(); } return lastIter.next(); } @Override public void remove() { if (lastIter == null) { throw new NoSuchElementException(); } lastIter.remove(); // This next statement would be the equivalent of: // if (index == highestPriority && levels[index].size() == 0) // However we have to keep checking all the previous levels // otherwise we would cache a max that will not exist // what would make us eventually having hasNext() returning false // as a bug // Part of the fix for HORNETQ-705 for (int i = index; i >= 0 && levels[index].size() == 0; i--) { highestPriority = i; } size--; } } }