| /* |
| * 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.ignite.internal.util; |
| |
| import java.util.AbstractCollection; |
| import java.util.ConcurrentModificationException; |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| import java.util.Queue; |
| import org.apache.ignite.internal.util.tostring.GridToStringExclude; |
| import org.apache.ignite.internal.util.typedef.internal.A; |
| import org.apache.ignite.internal.util.typedef.internal.S; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * Queue which supports addition at tail and removing at head. This |
| * queue also exposes its internal linked list nodes and allows for |
| * constant time removal from the middle of the queue. |
| * <p> |
| * This queue is not thread-safe. |
| */ |
| public class GridQueue<E> extends AbstractCollection<E> implements Queue<E> { |
| /** Queue size. */ |
| private int size; |
| |
| /** Modification count. */ |
| private int modCnt; |
| |
| /** Queue header. */ |
| private Node<E> hdr = new Node<>(null, null, null); |
| |
| /** |
| * Creates empty queue. |
| */ |
| public GridQueue() { |
| hdr.next = hdr.prev = hdr; |
| } |
| |
| /** |
| * Handles modification count check. |
| * |
| * @param match Modification count to match. |
| */ |
| private void checkModCount(int match) { |
| if (modCnt != match) |
| throw new ConcurrentModificationException("Mod count mismatch [expected=" + match + |
| ", actual=" + modCnt + ']'); |
| |
| modCnt++; |
| } |
| |
| /** |
| * Adds element before node. |
| * |
| * @param e Element to add. |
| * @param n Node. |
| * @return New node. |
| */ |
| private Node<E> addBefore(E e, Node<E> n) { |
| A.notNull(e, "e"); |
| |
| assert n != null; |
| |
| int match = modCnt; |
| |
| Node<E> newNode = new Node<>(e, n, n.prev); |
| |
| // Link. |
| newNode.prev.next = newNode; |
| newNode.next.prev = newNode; |
| |
| size++; |
| |
| checkModCount(match); |
| |
| return newNode; |
| } |
| |
| /** |
| * Removes node. |
| * |
| * @param n Node to remove. |
| * @return Removed value. |
| */ |
| private E remove(Node<E> n) { |
| assert n != null; |
| |
| if (n == hdr) |
| throw new NoSuchElementException(); |
| |
| assert !n.unlinked(); |
| |
| int match = modCnt; |
| |
| E res = n.item; |
| |
| // Relink. |
| n.prev.next = n.next; |
| n.next.prev = n.prev; |
| |
| // GC. |
| n.next = n.prev = null; |
| n.item = null; |
| |
| size--; |
| |
| checkModCount(match); |
| |
| n.unlink(); |
| |
| return res; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean add(E e) { |
| offer(e); |
| |
| return true; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean remove(Object o) { |
| A.notNull(o, "o"); |
| |
| for (Node<E> n = hdr.next; n != hdr; n = n.next) { |
| if (o.equals(n.item)) { |
| remove(n); |
| |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean offer(E e) { |
| addBefore(e, hdr); |
| |
| return true; |
| } |
| |
| /** |
| * Same as {@link #offer(Object)}, but returns created node. |
| * |
| * @param e Element to add. |
| * @return New node. |
| */ |
| public Node<E> offerx(E e) { |
| return addBefore(e, hdr); |
| } |
| |
| /** |
| * Polls element from head of the queue. |
| * |
| * @return Polled element. |
| */ |
| @Nullable @Override public E poll() { |
| if (size == 0) |
| return null; |
| |
| return remove(hdr.next); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public E element() { |
| Node<E> n = hdr.next; |
| |
| if (n == null) |
| throw new NoSuchElementException(); |
| |
| return n.item; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public E remove() { |
| E item = poll(); |
| |
| if (item == null) |
| throw new NoSuchElementException(); |
| |
| return item; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public E peek() { |
| return hdr.next.item; |
| } |
| |
| /** |
| * @return Peeks at first node in the queue. |
| */ |
| public Node<E> peekx() { |
| return hdr.next == hdr ? null : hdr.next; |
| } |
| |
| /** |
| * Unlinks node from the queue. |
| * |
| * @param n Node to unlink. |
| */ |
| public void unlink(Node<E> n) { |
| A.notNull(n, "n"); |
| |
| remove(n); |
| } |
| |
| /** |
| * Gets queue size. |
| * |
| * @return Queue size. |
| */ |
| @Override public int size() { |
| return size; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Iterator<E> iterator() { |
| return new QueueIterator(); |
| } |
| |
| /** |
| * Node for internal linked list. |
| * |
| * @param <E> Queue element. |
| */ |
| @SuppressWarnings( {"PublicInnerClass"}) |
| public static class Node<E> { |
| /** Item. */ |
| private E item; |
| |
| /** Next. */ |
| @GridToStringExclude |
| private Node<E> next; |
| |
| /** Previous. */ |
| @GridToStringExclude |
| private Node<E> prev; |
| |
| /** Unlinked flag. */ |
| private boolean unlinked; |
| |
| /** |
| * @param item Item. |
| * @param next Next link. |
| * @param prev Previous link. |
| */ |
| private Node(E item, Node<E> next, Node<E> prev) { |
| this.item = item; |
| this.next = next; |
| this.prev = prev; |
| } |
| |
| /** |
| * Gets this node's item. |
| * |
| * @return This node's item. |
| */ |
| public E item() { |
| return item; |
| } |
| |
| /** |
| * Sets unlinked flag. |
| */ |
| void unlink() { |
| assert !unlinked; |
| |
| unlinked = true; |
| } |
| |
| /** |
| * Checks if node is unlinked. |
| * |
| * @return {@code True} if node is unlinked. |
| */ |
| public boolean unlinked() { |
| return unlinked; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public String toString() { |
| return S.toString(Node.class, this); |
| } |
| } |
| |
| /** |
| * Iterator. |
| */ |
| private class QueueIterator implements Iterator<E> { |
| /** Next element. */ |
| private Node<E> next; |
| |
| /** Expected modification count. */ |
| private int expModCnt = modCnt; |
| |
| /** |
| * |
| */ |
| QueueIterator() { |
| next = hdr.next; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean hasNext() { |
| return next != hdr; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public E next() { |
| checkModCount(); |
| |
| if (next == null) |
| throw new NoSuchElementException(); |
| |
| E ret = next.item; |
| |
| next = next.next; |
| |
| return ret; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Checks modification count. |
| */ |
| private void checkModCount() { |
| if (modCnt != expModCnt) |
| throw new ConcurrentModificationException("Mod count mismatch [expected=" + expModCnt + |
| ", actual=" + modCnt + ']'); |
| } |
| } |
| } |