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

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

There is a newer version: 5.0.0.Alpha2
Show newest version
/*
 * Copyright 2015 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.
 */
/*
 * 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;

/**
 * Forked from JCTools.
 *
 * A Multi-Producer-Single-Consumer queue based on a {@link ConcurrentCircularArrayQueue}. This implies that
 * any thread may call the offer method, but only a single thread may call poll/peek for correctness to
 * maintained. 
* This implementation follows patterns documented on the package level for False Sharing protection.
* This implementation is using the Fast Flow * method for polling from the queue (with minor change to correctly publish the index) and an extension of * the Leslie Lamport concurrent queue algorithm (originated by Martin Thompson) on the producer side.
* * @param */ final class MpscArrayQueue extends MpscArrayQueueConsumerField { long p40, p41, p42, p43, p44, p45, p46; long p30, p31, p32, p33, p34, p35, p36, p37; public MpscArrayQueue(final int capacity) { super(capacity); } /** * {@inheritDoc}
* * IMPLEMENTATION NOTES:
* Lock free offer using a single CAS. As class name suggests access is permitted to many threads * concurrently. * * @see java.util.Queue#offer(java.lang.Object) */ @Override public boolean offer(final E e) { if (null == e) { throw new NullPointerException("Null is not a valid element"); } // use a cached view on consumer index (potentially updated in loop) final long mask = this.mask; final long capacity = mask + 1; long consumerIndexCache = lvConsumerIndexCache(); // LoadLoad long currentProducerIndex; do { currentProducerIndex = lvProducerIndex(); // LoadLoad final long wrapPoint = currentProducerIndex - capacity; if (consumerIndexCache <= wrapPoint) { final long currHead = lvConsumerIndex(); // LoadLoad if (currHead <= wrapPoint) { return false; // FULL :( } else { // update shared cached value of the consumerIndex svConsumerIndexCache(currHead); // StoreLoad // update on stack copy, we might need this value again if we lose the CAS. consumerIndexCache = currHead; } } } while (!casProducerIndex(currentProducerIndex, currentProducerIndex + 1)); /* * NOTE: the new producer index value is made visible BEFORE the element in the array. If we relied on * the index visibility to poll() we would need to handle the case where the element is not visible. */ // Won CAS, move on to storing final long offset = calcElementOffset(currentProducerIndex, mask); soElement(offset, e); // StoreStore return true; // AWESOME :) } /** * A wait free alternative to offer which fails on CAS failure. * * @param e new element, not null * @return 1 if next element cannot be filled, -1 if CAS failed, 0 if successful */ public int weakOffer(final E e) { if (null == e) { throw new NullPointerException("Null is not a valid element"); } final long mask = this.mask; final long capacity = mask + 1; final long currentTail = lvProducerIndex(); // LoadLoad final long consumerIndexCache = lvConsumerIndexCache(); // LoadLoad final long wrapPoint = currentTail - capacity; if (consumerIndexCache <= wrapPoint) { long currHead = lvConsumerIndex(); // LoadLoad if (currHead <= wrapPoint) { return 1; // FULL :( } else { svConsumerIndexCache(currHead); // StoreLoad } } // look Ma, no loop! if (!casProducerIndex(currentTail, currentTail + 1)) { return -1; // CAS FAIL :( } // Won CAS, move on to storing final long offset = calcElementOffset(currentTail, mask); soElement(offset, e); return 0; // AWESOME :) } /** * {@inheritDoc} *

* IMPLEMENTATION NOTES:
* Lock free poll using ordered loads/stores. As class name suggests access is limited to a single thread. * * @see java.util.Queue#poll() */ @Override public E poll() { final long consumerIndex = lvConsumerIndex(); // LoadLoad final long offset = calcElementOffset(consumerIndex); // Copy field to avoid re-reading after volatile load final E[] buffer = this.buffer; // If we can't see the next available element we can't poll E e = lvElement(buffer, offset); // LoadLoad if (null == e) { /* * NOTE: Queue may not actually be empty in the case of a producer (P1) being interrupted after * winning the CAS on offer but before storing the element in the queue. Other producers may go on * to fill up the queue after this element. */ if (consumerIndex != lvProducerIndex()) { do { e = lvElement(buffer, offset); } while (e == null); } else { return null; } } spElement(buffer, offset, null); soConsumerIndex(consumerIndex + 1); // StoreStore return e; } /** * {@inheritDoc} *

* IMPLEMENTATION NOTES:
* Lock free peek using ordered loads. As class name suggests access is limited to a single thread. * * @see java.util.Queue#poll() */ @Override public E peek() { // Copy field to avoid re-reading after volatile load final E[] buffer = this.buffer; final long consumerIndex = lvConsumerIndex(); // LoadLoad final long offset = calcElementOffset(consumerIndex); E e = lvElement(buffer, offset); if (null == e) { /* * NOTE: Queue may not actually be empty in the case of a producer (P1) being interrupted after * winning the CAS on offer but before storing the element in the queue. Other producers may go on * to fill up the queue after this element. */ if (consumerIndex != lvProducerIndex()) { do { e = lvElement(buffer, offset); } while (e == null); } else { return null; } } return e; } /** * {@inheritDoc} *

* */ @Override public int size() { /* * It is possible for a thread to be interrupted or reschedule between the read of the producer and * consumer indices, therefore protection is required to ensure size is within valid range. In the * event of concurrent polls/offers to this method the size is OVER estimated as we read consumer * index BEFORE the producer index. */ long after = lvConsumerIndex(); while (true) { final long before = after; final long currentProducerIndex = lvProducerIndex(); after = lvConsumerIndex(); if (before == after) { return (int) (currentProducerIndex - after); } } } @Override public boolean isEmpty() { // Order matters! // Loading consumer before producer allows for producer increments after consumer index is read. // This ensures the correctness of this method at least for the consumer thread. Other threads POV is // not really // something we can fix here. return lvConsumerIndex() == lvProducerIndex(); } } abstract class MpscArrayQueueL1Pad extends ConcurrentCircularArrayQueue { long p10, p11, p12, p13, p14, p15, p16; long p30, p31, p32, p33, p34, p35, p36, p37; public MpscArrayQueueL1Pad(int capacity) { super(capacity); } } abstract class MpscArrayQueueTailField extends MpscArrayQueueL1Pad { private static final long P_INDEX_OFFSET; static { try { P_INDEX_OFFSET = PlatformDependent0.UNSAFE.objectFieldOffset(MpscArrayQueueTailField.class .getDeclaredField("producerIndex")); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } private volatile long producerIndex; public MpscArrayQueueTailField(int capacity) { super(capacity); } protected final long lvProducerIndex() { return producerIndex; } protected final boolean casProducerIndex(long expect, long newValue) { return PlatformDependent0.UNSAFE.compareAndSwapLong(this, P_INDEX_OFFSET, expect, newValue); } } abstract class MpscArrayQueueMidPad extends MpscArrayQueueTailField { long p20, p21, p22, p23, p24, p25, p26; long p30, p31, p32, p33, p34, p35, p36, p37; public MpscArrayQueueMidPad(int capacity) { super(capacity); } } abstract class MpscArrayQueueHeadCacheField extends MpscArrayQueueMidPad { private volatile long headCache; public MpscArrayQueueHeadCacheField(int capacity) { super(capacity); } protected final long lvConsumerIndexCache() { return headCache; } protected final void svConsumerIndexCache(long v) { headCache = v; } } abstract class MpscArrayQueueL2Pad extends MpscArrayQueueHeadCacheField { long p20, p21, p22, p23, p24, p25, p26; long p30, p31, p32, p33, p34, p35, p36, p37; public MpscArrayQueueL2Pad(int capacity) { super(capacity); } } abstract class MpscArrayQueueConsumerField extends MpscArrayQueueL2Pad { private static final long C_INDEX_OFFSET; static { try { C_INDEX_OFFSET = PlatformDependent0.UNSAFE.objectFieldOffset(MpscArrayQueueConsumerField.class .getDeclaredField("consumerIndex")); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } private volatile long consumerIndex; public MpscArrayQueueConsumerField(int capacity) { super(capacity); } protected final long lvConsumerIndex() { return consumerIndex; } protected void soConsumerIndex(long l) { PlatformDependent0.UNSAFE.putOrderedLong(this, C_INDEX_OFFSET, l); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy