All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.netty.util.internal.MpscLinkedQueue Maven / Gradle / Ivy

There is a newer version: 5.0.0.Alpha2
Show newest version
/*
 * 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.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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)}, {@link #addAll(Collection)}
  • *
  • {@link #isEmpty()}
  • *
* .. while only one consumer thread is allowed to perform the following operations exclusively: *
    *
  • {@link #poll()} and {@link #remove()}
  • *
  • {@link #element()}, {@link #peek()}
  • *
  • {@link #remove(Object)}, {@link #removeAll(Collection)}, and {@link #retainAll(Collection)}
  • *
  • {@link #clear()}
  • {@link #} *
  • {@link #iterator()}
  • *
  • {@link #toArray()} and {@link #toArray(Object[])}
  • *
  • {@link #contains(Object)} and {@link #containsAll(Collection)}
  • *
  • {@link #size()}
  • *
* * 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: * * and adopted padded head node changes from: * * 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 value == null it means that clearMaybe() was called on the MpscLinkedQueueNode. if (n == null || n.value() == null) { break; } MpscLinkedQueueNode next = n.next(); if (n == next) { break; } n = next; if (++ count == Integer.MAX_VALUE) { // Guard against overflow of integer. break; } } return count; } @Override public boolean isEmpty() { return headRef() == tailRef(); } @Override public boolean contains(Object o) { MpscLinkedQueueNode n = peekNode(); for (;;) { if (n == null) { break; } E value = n.value(); // If value == null it means that clearMaybe() was called on the MpscLinkedQueueNode. if (value == null) { return false; } if (value == o) { return true; } MpscLinkedQueueNode next = n.next(); if (n == next) { break; } n = next; } return false; } @Override public Iterator iterator() { return new ReadOnlyIterator(toList().iterator()); } @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(); } private List toList(int initialCapacity) { return toList(new ArrayList(initialCapacity)); } private List toList() { return toList(new ArrayList()); } private List toList(List elements) { MpscLinkedQueueNode n = peekNode(); for (;;) { if (n == null) { break; } E value = n.value(); if (value == null) { break; } if (!elements.add(value)) { // Seems like there is no space left, break here. break; } MpscLinkedQueueNode next = n.next(); if (n == next) { break; } n = next; } return elements; } @Override public Object[] toArray() { return toList().toArray(); } @Override @SuppressWarnings("unchecked") public T[] toArray(T[] a) { return toList(a.length).toArray(a); } @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 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 - 2024 Weber Informatics LLC | Privacy Policy