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

rx.internal.util.unsafe.SpscUnboundedArrayQueue Maven / Gradle / Ivy

There is a newer version: 1.3.8
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.
 * 
 * Original License: https://github.com/JCTools/JCTools/blob/master/LICENSE
 * Original location: https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/SpscUnboundedArrayQueue.java
 */
package rx.internal.util.unsafe;

import static rx.internal.util.unsafe.UnsafeAccess.*;

import java.lang.reflect.Field;
import java.util.AbstractQueue;
import java.util.Iterator;

import rx.internal.util.SuppressAnimalSniffer;

abstract class SpscUnboundedArrayQueueProducerFields extends AbstractQueue {
    protected long producerIndex;
}

abstract class SpscUnboundedArrayQueueProducerColdFields extends SpscUnboundedArrayQueueProducerFields {
    protected int producerLookAheadStep;
    protected long producerLookAhead;
    protected long producerMask;
    protected E[] producerBuffer;
}

abstract class SpscUnboundedArrayQueueL2Pad extends SpscUnboundedArrayQueueProducerColdFields {
    long p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12;
}

abstract class SpscUnboundedArrayQueueConsumerColdField extends SpscUnboundedArrayQueueL2Pad {
    protected long consumerMask;
    protected E[] consumerBuffer;
}

abstract class SpscUnboundedArrayQueueConsumerField extends SpscUnboundedArrayQueueConsumerColdField {
    protected long consumerIndex;
}

@SuppressAnimalSniffer
public class SpscUnboundedArrayQueue extends SpscUnboundedArrayQueueConsumerField
    implements QueueProgressIndicators{
    static final int MAX_LOOK_AHEAD_STEP = Integer.getInteger("jctools.spsc.max.lookahead.step", 4096);
    private final static long P_INDEX_OFFSET;
    private final static long C_INDEX_OFFSET;
    private static final long REF_ARRAY_BASE;
    private static final int REF_ELEMENT_SHIFT;
    private static final Object HAS_NEXT = new Object();
    static {
        final int scale = UnsafeAccess.UNSAFE.arrayIndexScale(Object[].class);
        if (4 == scale) {
            REF_ELEMENT_SHIFT = 2;
        } else if (8 == scale) {
            REF_ELEMENT_SHIFT = 3;
        } else {
            throw new IllegalStateException("Unknown pointer size");
        }
        // Including the buffer pad in the array base offset
        REF_ARRAY_BASE = UnsafeAccess.UNSAFE.arrayBaseOffset(Object[].class);
        try {
            Field iField = SpscUnboundedArrayQueueProducerFields.class.getDeclaredField("producerIndex");
            P_INDEX_OFFSET = UNSAFE.objectFieldOffset(iField);
        } catch (NoSuchFieldException e) {
            InternalError ex = new InternalError();
            ex.initCause(e);
            throw ex;
        }
        try {
            Field iField = SpscUnboundedArrayQueueConsumerField.class.getDeclaredField("consumerIndex");
            C_INDEX_OFFSET = UNSAFE.objectFieldOffset(iField);
        } catch (NoSuchFieldException e) {
            InternalError ex = new InternalError();
            ex.initCause(e);
            throw ex;
        }
    }

    @SuppressWarnings("unchecked")
    public SpscUnboundedArrayQueue(final int bufferSize) {
        int p2capacity = Pow2.roundToPowerOfTwo(bufferSize);
        long mask = p2capacity - 1;
        E[] buffer = (E[]) new Object[p2capacity + 1];
        producerBuffer = buffer;
        producerMask = mask;
        adjustLookAheadStep(p2capacity);
        consumerBuffer = buffer;
        consumerMask = mask;
        producerLookAhead = mask - 1; // we know it's all empty to start with
        soProducerIndex(0l);
    }

    @Override
    public final Iterator iterator() {
        throw new UnsupportedOperationException();
    }

    /**
     * {@inheritDoc}
     * 

* This implementation is correct for single producer thread use only. */ @Override public final boolean offer(final E e) { if (null == e) { throw new NullPointerException("Null is not a valid element"); } // local load of field to avoid repeated loads after volatile reads final E[] buffer = producerBuffer; final long index = producerIndex; final long mask = producerMask; final long offset = calcWrappedOffset(index, mask); if (index < producerLookAhead) { return writeToQueue(buffer, e, index, offset); } else { final int lookAheadStep = producerLookAheadStep; // go around the buffer or resize if full (unless we hit max capacity) long lookAheadElementOffset = calcWrappedOffset(index + lookAheadStep, mask); if (null == lvElement(buffer, lookAheadElementOffset)) {// LoadLoad producerLookAhead = index + lookAheadStep - 1; // joy, there's plenty of room return writeToQueue(buffer, e, index, offset); } else if (null != lvElement(buffer, calcWrappedOffset(index + 1, mask))) { // buffer is not full return writeToQueue(buffer, e, index, offset); } else { resize(buffer, index, offset, e, mask); // add a buffer and link old to new return true; } } } private boolean writeToQueue(final E[] buffer, final E e, final long index, final long offset) { soElement(buffer, offset, e);// StoreStore soProducerIndex(index + 1);// this ensures atomic write of long on 32bit platforms return true; } @SuppressWarnings("unchecked") private void resize(final E[] oldBuffer, final long currIndex, final long offset, final E e, final long mask) { final int capacity = oldBuffer.length; final E[] newBuffer = (E[]) new Object[capacity]; producerBuffer = newBuffer; producerLookAhead = currIndex + mask - 1; soElement(newBuffer, offset, e);// StoreStore soNext(oldBuffer, newBuffer); soElement(oldBuffer, offset, HAS_NEXT); // new buffer is visible after element is // inserted soProducerIndex(currIndex + 1);// this ensures correctness on 32bit platforms } private void soNext(E[] curr, E[] next) { soElement(curr, calcDirectOffset(curr.length -1), next); } @SuppressWarnings("unchecked") private E[] lvNext(E[] curr) { return (E[]) lvElement(curr, calcDirectOffset(curr.length -1)); } /** * {@inheritDoc} *

* This implementation is correct for single consumer thread use only. */ @SuppressWarnings("unchecked") @Override public final E poll() { // local load of field to avoid repeated loads after volatile reads final E[] buffer = consumerBuffer; final long index = consumerIndex; final long mask = consumerMask; final long offset = calcWrappedOffset(index, mask); final Object e = lvElement(buffer, offset);// LoadLoad boolean isNextBuffer = e == HAS_NEXT; if (null != e && !isNextBuffer) { soElement(buffer, offset, null);// StoreStore soConsumerIndex(index + 1);// this ensures correctness on 32bit platforms return (E) e; } else if (isNextBuffer) { return newBufferPoll(lvNext(buffer), index, mask); } return null; } @SuppressWarnings("unchecked") private E newBufferPoll(E[] nextBuffer, final long index, final long mask) { consumerBuffer = nextBuffer; final long offsetInNew = calcWrappedOffset(index, mask); final E n = (E) lvElement(nextBuffer, offsetInNew);// LoadLoad if (null == n) { return null; } else { soElement(nextBuffer, offsetInNew, null);// StoreStore soConsumerIndex(index + 1);// this ensures correctness on 32bit platforms return n; } } /** * {@inheritDoc} *

* This implementation is correct for single consumer thread use only. */ @SuppressWarnings("unchecked") @Override public final E peek() { final E[] buffer = consumerBuffer; final long index = consumerIndex; final long mask = consumerMask; final long offset = calcWrappedOffset(index, mask); final Object e = lvElement(buffer, offset);// LoadLoad if (e == HAS_NEXT) { return newBufferPeek(lvNext(buffer), index, mask); } return (E) e; } @SuppressWarnings("unchecked") private E newBufferPeek(E[] nextBuffer, final long index, final long mask) { consumerBuffer = nextBuffer; final long offsetInNew = calcWrappedOffset(index, mask); return (E) lvElement(nextBuffer, offsetInNew);// LoadLoad } @Override public final 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); } } } private void adjustLookAheadStep(int capacity) { producerLookAheadStep = Math.min(capacity / 4, MAX_LOOK_AHEAD_STEP); } private long lvProducerIndex() { return UNSAFE.getLongVolatile(this, P_INDEX_OFFSET); } private long lvConsumerIndex() { return UNSAFE.getLongVolatile(this, C_INDEX_OFFSET); } private void soProducerIndex(long v) { UNSAFE.putOrderedLong(this, P_INDEX_OFFSET, v); } private void soConsumerIndex(long v) { UNSAFE.putOrderedLong(this, C_INDEX_OFFSET, v); } private static long calcWrappedOffset(long index, long mask) { return calcDirectOffset(index & mask); } private static long calcDirectOffset(long index) { return REF_ARRAY_BASE + (index << REF_ELEMENT_SHIFT); } private static void soElement(Object[] buffer, long offset, Object e) { UNSAFE.putOrderedObject(buffer, offset, e); } private static Object lvElement(E[] buffer, long offset) { return UNSAFE.getObjectVolatile(buffer, offset); } @Override public long currentProducerIndex() { return lvProducerIndex(); } @Override public long currentConsumerIndex() { return lvConsumerIndex(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy