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

io.netty.util.internal.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue Maven / Gradle / Ivy

There is a newer version: 1.15.4
Show newest version
/*
 * 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 io.netty.util.internal.shaded.org.jctools.queues.atomic;

import java.util.Queue;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceArray;
import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue;
import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue.Supplier;
import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueueUtil;
import io.netty.util.internal.shaded.org.jctools.queues.QueueProgressIndicators;
import io.netty.util.internal.shaded.org.jctools.queues.IndexedQueueSizeUtil;
import static io.netty.util.internal.shaded.org.jctools.queues.atomic.AtomicQueueUtil.*;

/**
 * NOTE: This class was automatically generated by io.netty.util.internal.shaded.org.jctools.queues.atomic.JavaParsingAtomicLinkedQueueGenerator
 * which can found in the jctools-build module. The original source file is MpscLinkedQueue.java.
 *
 * This is a Java port of the MPSC algorithm as presented
 *  on
 * 1024 Cores by D. Vyukov. The original has been adapted to Java and it's quirks with regards to memory
 * model and layout:
 * 
    *
  1. Use inheritance to ensure no false sharing occurs between producer/consumer node reference fields. *
  2. Use XCHG functionality to the best of the JDK ability (see differences in JDK7/8 impls). *
  3. Conform to {@link java.util.Queue} contract on poll. The original semantics are available via relaxedPoll. *
* The queue is initialized with a stub node which is set to both the producer and consumer node references. * From this point follow the notes on offer/poll. */ public class MpscLinkedAtomicQueue extends BaseLinkedAtomicQueue { public MpscLinkedAtomicQueue() { LinkedQueueAtomicNode node = newNode(); spConsumerNode(node); xchgProducerNode(node); } /** * {@inheritDoc}
*

* IMPLEMENTATION NOTES:
* Offer is allowed from multiple threads.
* Offer allocates a new node and: *

    *
  1. Swaps it atomically with current producer node (only one producer 'wins') *
  2. Sets the new node as the node following from the swapped producer node *
* This works because each producer is guaranteed to 'plant' a new node and link the old node. No 2 * producers can get the same producer node as part of XCHG guarantee. * * @see MessagePassingQueue#offer(Object) * @see java.util.Queue#offer(java.lang.Object) */ @Override public boolean offer(final E e) { if (null == e) { throw new NullPointerException(); } final LinkedQueueAtomicNode nextNode = newNode(e); final LinkedQueueAtomicNode prevProducerNode = xchgProducerNode(nextNode); // Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed // and completes the store in prev.next. This is a "bubble". prevProducerNode.soNext(nextNode); return true; } /** * {@inheritDoc}
*

* IMPLEMENTATION NOTES:
* Poll is allowed from a SINGLE thread.
* Poll is potentially blocking here as the {@link Queue#poll()} does not allow returning {@code null} if the queue is not * empty. This is very different from the original Vyukov guarantees. See {@link #relaxedPoll()} for the original * semantics.
* Poll reads {@code consumerNode.next} and: *

    *
  1. If it is {@code null} AND the queue is empty return {@code null}, if queue is not empty spin wait for * value to become visible. *
  2. If it is not {@code null} set it as the consumer node and return it's now evacuated value. *
* This means the consumerNode.value is always {@code null}, which is also the starting point for the queue. * Because {@code null} values are not allowed to be offered this is the only node with it's value set to * {@code null} at any one time. * * @see MessagePassingQueue#poll() * @see java.util.Queue#poll() */ @Override public E poll() { final LinkedQueueAtomicNode currConsumerNode = lpConsumerNode(); LinkedQueueAtomicNode nextNode = currConsumerNode.lvNext(); if (nextNode != null) { return getSingleConsumerNodeValue(currConsumerNode, nextNode); } else if (currConsumerNode != lvProducerNode()) { nextNode = spinWaitForNextNode(currConsumerNode); // got the next node... return getSingleConsumerNodeValue(currConsumerNode, nextNode); } return null; } /** * {@inheritDoc}
*

* IMPLEMENTATION NOTES:
* Peek is allowed from a SINGLE thread.
* Peek is potentially blocking here as the {@link Queue#peek()} does not allow returning {@code null} if the queue is not * empty. This is very different from the original Vyukov guarantees. See {@link #relaxedPeek()} for the original * semantics.
* Poll reads the next node from the consumerNode and: *

    *
  1. If it is {@code null} AND the queue is empty return {@code null}, if queue is not empty spin wait for * value to become visible. *
  2. If it is not {@code null} return it's value. *
* * @see MessagePassingQueue#peek() * @see java.util.Queue#peek() */ @Override public E peek() { final LinkedQueueAtomicNode currConsumerNode = lpConsumerNode(); LinkedQueueAtomicNode nextNode = currConsumerNode.lvNext(); if (nextNode != null) { return nextNode.lpValue(); } else if (currConsumerNode != lvProducerNode()) { nextNode = spinWaitForNextNode(currConsumerNode); // got the next node... return nextNode.lpValue(); } return null; } /** * {@inheritDoc} *

* This method is only safe to call from the (single) consumer thread, and is subject to best effort when racing * with producers. This method is potentially blocking when "bubble"s in the queue are visible. */ @Override public boolean remove(Object o) { if (null == o) { // Null elements are not permitted, so null will never be removed. return false; } final LinkedQueueAtomicNode originalConsumerNode = lpConsumerNode(); LinkedQueueAtomicNode prevConsumerNode = originalConsumerNode; LinkedQueueAtomicNode currConsumerNode = getNextConsumerNode(originalConsumerNode); while (currConsumerNode != null) { if (o.equals(currConsumerNode.lpValue())) { LinkedQueueAtomicNode nextNode = getNextConsumerNode(currConsumerNode); // e.g.: consumerNode -> node0 -> node1(o==v) -> node2 ... => consumerNode -> node0 -> node2 if (nextNode != null) { // We are removing an interior node. prevConsumerNode.soNext(nextNode); } else // This case reflects: prevConsumerNode != originalConsumerNode && nextNode == null // At rest, this would be the producerNode, but we must contend with racing. Changes to subclassed // queues need to consider remove() when implementing offer(). { // producerNode is currConsumerNode, try to atomically update the reference to move it to the // previous node. prevConsumerNode.soNext(null); if (!casProducerNode(currConsumerNode, prevConsumerNode)) { // If the producer(s) have offered more items we need to remove the currConsumerNode link. nextNode = spinWaitForNextNode(currConsumerNode); prevConsumerNode.soNext(nextNode); } } // Avoid GC nepotism because we are discarding the current node. currConsumerNode.soNext(null); currConsumerNode.spValue(null); return true; } prevConsumerNode = currConsumerNode; currConsumerNode = getNextConsumerNode(currConsumerNode); } return false; } @Override public int fill(Supplier s) { return MessagePassingQueueUtil.fillUnbounded(this, s); } @Override public int fill(Supplier s, int limit) { if (null == s) throw new IllegalArgumentException("supplier is null"); if (limit < 0) throw new IllegalArgumentException("limit is negative:" + limit); if (limit == 0) return 0; LinkedQueueAtomicNode tail = newNode(s.get()); final LinkedQueueAtomicNode head = tail; for (int i = 1; i < limit; i++) { final LinkedQueueAtomicNode temp = newNode(s.get()); tail.soNext(temp); tail = temp; } final LinkedQueueAtomicNode oldPNode = xchgProducerNode(tail); oldPNode.soNext(head); return limit; } @Override public void fill(Supplier s, WaitStrategy wait, ExitCondition exit) { MessagePassingQueueUtil.fill(this, s, wait, exit); } private LinkedQueueAtomicNode getNextConsumerNode(LinkedQueueAtomicNode currConsumerNode) { LinkedQueueAtomicNode nextNode = currConsumerNode.lvNext(); if (nextNode == null && currConsumerNode != lvProducerNode()) { nextNode = spinWaitForNextNode(currConsumerNode); } return nextNode; } private LinkedQueueAtomicNode spinWaitForNextNode(LinkedQueueAtomicNode currNode) { LinkedQueueAtomicNode nextNode; while ((nextNode = currNode.lvNext()) == null) { // spin, we are no longer wait free } return nextNode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy