io.netty.util.internal.MpscLinkedQueue Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* Copyright 2014 The Netty Project
*
* The Netty Project 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.
*/
/**
* Copyright (C) 2009-2013 Typesafe Inc.
*/
package io.netty.util.internal;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* A lock-free concurrent single-consumer multi-producer {@link Queue}.
* It allows multiple producer threads to perform the following operations simultaneously:
*
* - {@link #offer(Object)}, {@link #add(Object)}, and {@link #addAll(Collection)}
* - All other read-only operations:
*
* - {@link #contains(Object)} and {@link #containsAll(Collection)}
* - {@link #element()}, {@link #peek()}
* - {@link #size()} and {@link #isEmpty()}
* - {@link #iterator()} (except {@link Iterator#remove()}
* - {@link #toArray()} and {@link #toArray(Object[])}
*
*
*
* .. while only one consumer thread is allowed to perform the following operations exclusively:
*
* - {@link #poll()} and {@link #remove()}
* - {@link #remove(Object)}, {@link #removeAll(Collection)}, and {@link #retainAll(Collection)}
* - {@link #clear()}
{@link #}
*
*
* The behavior of this implementation is undefined if you perform the operations for a consumer thread only
* from multiple threads.
*
* The initial implementation is based on:
*
* - Non-intrusive MPSC node based queue from 1024cores.net
* - AbstractNodeQueue from Akka
*
* and adopted padded head node changes from:
*
* - MpscPaddedQueue from RxJava
*
* data structure modified to avoid false sharing between head and tail Ref as per implementation of MpscLinkedQueue
* on JCTools project.
*/
final class MpscLinkedQueue extends MpscLinkedQueueTailRef implements Queue {
private static final long serialVersionUID = -1878402552271506449L;
long p00, p01, p02, p03, p04, p05, p06, p07;
long p30, p31, p32, p33, p34, p35, p36, p37;
// offer() occurs at the tail of the linked list.
// poll() occurs at the head of the linked list.
//
// Resulting layout is:
//
// head --next--> 1st element --next--> 2nd element --next--> ... tail (last element)
//
// where the head is a dummy node whose value is null.
//
// offer() appends a new node next to the tail using AtomicReference.getAndSet()
// poll() removes head from the linked list and promotes the 1st element to the head,
// setting its value to null if possible.
//
// Also note that this class extends AtomicReference for the "tail" slot (which is the one that is appended to)
// since Unsafe does not expose XCHG operation intrinsically.
MpscLinkedQueue() {
MpscLinkedQueueNode tombstone = new DefaultNode(null);
setHeadRef(tombstone);
setTailRef(tombstone);
}
/**
* Returns the node right next to the head, which contains the first element of this queue.
*/
private MpscLinkedQueueNode peekNode() {
MpscLinkedQueueNode head = headRef();
MpscLinkedQueueNode next = head.next();
if (next == null && head != tailRef()) {
// if tail != head this is not going to change until consumer makes progress
// we can avoid reading the head and just spin on next until it shows up
//
// See https://github.com/akka/akka/pull/15596
do {
next = head.next();
} while (next == null);
}
return next;
}
@Override
@SuppressWarnings("unchecked")
public boolean offer(E value) {
if (value == null) {
throw new NullPointerException("value");
}
final MpscLinkedQueueNode newTail;
if (value instanceof MpscLinkedQueueNode) {
newTail = (MpscLinkedQueueNode) value;
newTail.setNext(null);
} else {
newTail = new DefaultNode(value);
}
MpscLinkedQueueNode oldTail = getAndSetTailRef(newTail);
oldTail.setNext(newTail);
return true;
}
@Override
public E poll() {
final MpscLinkedQueueNode next = peekNode();
if (next == null) {
return null;
}
// next becomes a new head.
MpscLinkedQueueNode oldHead = headRef();
// Similar to 'headRef.node = next', but slightly faster (storestore vs loadstore)
// See: http://robsjava.blogspot.com/2013/06/a-faster-volatile.html
// See: http://psy-lob-saw.blogspot.com/2012/12/atomiclazyset-is-performance-win-for.html
lazySetHeadRef(next);
// Break the linkage between the old head and the new head.
oldHead.unlink();
return next.clearMaybe();
}
@Override
public E peek() {
final MpscLinkedQueueNode next = peekNode();
if (next == null) {
return null;
}
return next.value();
}
@Override
public int size() {
int count = 0;
MpscLinkedQueueNode n = peekNode();
for (;;) {
if (n == null) {
break;
}
count ++;
n = n.next();
}
return count;
}
@Override
public boolean isEmpty() {
return peekNode() == null;
}
@Override
public boolean contains(Object o) {
MpscLinkedQueueNode n = peekNode();
for (;;) {
if (n == null) {
break;
}
if (n.value() == o) {
return true;
}
n = n.next();
}
return false;
}
@Override
public Iterator iterator() {
return new Iterator() {
private MpscLinkedQueueNode node = peekNode();
@Override
public boolean hasNext() {
return node != null;
}
@Override
public E next() {
MpscLinkedQueueNode node = this.node;
if (node == null) {
throw new NoSuchElementException();
}
E value = node.value();
this.node = node.next();
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean add(E e) {
if (offer(e)) {
return true;
}
throw new IllegalStateException("queue full");
}
@Override
public E remove() {
E e = poll();
if (e != null) {
return e;
}
throw new NoSuchElementException();
}
@Override
public E element() {
E e = peek();
if (e != null) {
return e;
}
throw new NoSuchElementException();
}
@Override
public Object[] toArray() {
final Object[] array = new Object[size()];
final Iterator it = iterator();
for (int i = 0; i < array.length; i ++) {
if (it.hasNext()) {
array[i] = it.next();
} else {
return Arrays.copyOf(array, i);
}
}
return array;
}
@Override
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
final int size = size();
final T[] array;
if (a.length >= size) {
array = a;
} else {
array = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
}
final Iterator it = iterator();
for (int i = 0; i < array.length; i++) {
if (it.hasNext()) {
array[i] = (T) it.next();
} else {
if (a == array) {
array[i] = null;
return array;
}
if (a.length < i) {
return Arrays.copyOf(array, i);
}
System.arraycopy(array, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
return a;
}
}
return array;
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection> c) {
for (Object e: c) {
if (!contains(e)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection extends E> c) {
if (c == null) {
throw new NullPointerException("c");
}
if (c == this) {
throw new IllegalArgumentException("c == this");
}
boolean modified = false;
for (E e: c) {
add(e);
modified = true;
}
return modified;
}
@Override
public boolean removeAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
while (poll() != null) {
continue;
}
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
for (E e: this) {
out.writeObject(e);
}
out.writeObject(null);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final MpscLinkedQueueNode tombstone = new DefaultNode(null);
setHeadRef(tombstone);
setTailRef(tombstone);
for (;;) {
@SuppressWarnings("unchecked")
E e = (E) in.readObject();
if (e == null) {
break;
}
add(e);
}
}
private static final class DefaultNode extends MpscLinkedQueueNode {
private T value;
DefaultNode(T value) {
this.value = value;
}
@Override
public T value() {
return value;
}
@Override
protected T clearMaybe() {
T value = this.value;
this.value = null;
return value;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy