@SuppressWarnings("unchecked") private AtomicReferenceArray<Object> lvNext(AtomicReferenceArray<Object> curr) { return (AtomicReferenceArray<Object>)lvElement(curr, calcDirectOffset(curr.length() - 1)); } @Nullable
@SuppressWarnings("unchecked") public T peek() { final AtomicReferenceArray<Object> buffer = consumerBuffer; final long index = lpConsumerIndex(); final int mask = consumerMask; final int offset = calcWrappedOffset(index, mask); final Object e = lvElement(buffer, offset);// LoadLoad if (e == HAS_NEXT) { return newBufferPeek(lvNext(buffer), index, mask); } return (T) e; }
@SuppressWarnings("unchecked") private T newBufferPeek(AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) { consumerBuffer = nextBuffer; final int offsetInNew = calcWrappedOffset(index, mask); return (T) lvElement(nextBuffer, offsetInNew);// LoadLoad }
@SuppressWarnings("unchecked") private T newBufferPoll(AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) { consumerBuffer = nextBuffer; final int offsetInNew = calcWrappedOffset(index, mask); final T n = (T) lvElement(nextBuffer, offsetInNew);// LoadLoad if (null != n) { soElement(nextBuffer, offsetInNew, null);// StoreStore soConsumerIndex(index + 1);// this ensures correctness on 32bit platforms } return n; }
@Nullable @SuppressWarnings("unchecked") public final T poll() { // local load of field to avoid repeated loads after volatile reads final AtomicReferenceArray<Object> buffer = consumerBuffer; final long index = lpConsumerIndex(); final int mask = consumerMask; final int 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 (T) e; } else if (isNextBuffer) { return newBufferPoll(lvNext(buffer), index, mask); } return null; }
final long p = lvProducerIndex(); final int m = producerMask; int pi = calcWrappedOffset(p + 2, m); if (null == lvElement(buffer, pi)) { pi = calcWrappedOffset(p, m); soElement(buffer, pi + 1, second); soElement(buffer, pi, first); soProducerIndex(p + 2); } else { final int capacity = buffer.length(); producerBuffer = newBuffer; pi = calcWrappedOffset(p, m); soElement(newBuffer, pi + 1, second);// StoreStore soElement(newBuffer, pi, first); soNext(buffer, newBuffer); soElement(buffer, pi, HAS_NEXT); // new buffer is visible after element is soProducerIndex(p + 2);// this ensures correctness on 32bit platforms
public final boolean offer(final T 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 AtomicReferenceArray<Object> buffer = producerBuffer; final long index = lpProducerIndex(); final int mask = producerMask; final int 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) int 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; } } }
public final boolean isEmpty() { return lvProducerIndex() == lvConsumerIndex(); }
public AbstractSpscLinkedArrayQueue(final int bufferSize) { int p2capacity = Pow2.roundToPowerOfTwo(Math.max(8, bufferSize)); int mask = p2capacity - 1; AtomicReferenceArray<Object> buffer = new AtomicReferenceArray<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); }
private void soNext(AtomicReferenceArray<Object> curr, AtomicReferenceArray<Object> next) { soElement(curr, calcDirectOffset(curr.length() - 1), next); } @SuppressWarnings("unchecked")
@Test public void spscLinkedNewBufferPeek() { AbstractSpscLinkedArrayQueue<Integer> q = new AbstractSpscLinkedArrayQueue<Integer>(8) { }; assertTrue(q.offer(1, 2)); assertTrue(q.offer(3, 4)); assertTrue(q.offer(5, 6)); assertTrue(q.offer(7, 8)); // this should trigger a new buffer for (int i = 0; i < 8; i++) { assertEquals(i + 1, q.peek().intValue()); assertEquals(i + 1, q.poll().intValue()); } assertNull(q.peek()); assertNull(q.poll()); }
private static int calcWrappedOffset(long index, int mask) { return calcDirectOffset((int)index & mask); } private static int calcDirectOffset(int index) {
@Nullable @SuppressWarnings("unchecked") public final T poll() { // local load of field to avoid repeated loads after volatile reads final AtomicReferenceArray<Object> buffer = consumerBuffer; final long index = lpConsumerIndex(); final int mask = consumerMask; final int 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 (T) e; } else if (isNextBuffer) { return newBufferPoll(lvNext(buffer), index, mask); } return null; }
final long p = lvProducerIndex(); final int m = producerMask; int pi = calcWrappedOffset(p + 2, m); if (null == lvElement(buffer, pi)) { pi = calcWrappedOffset(p, m); soElement(buffer, pi + 1, second); soElement(buffer, pi, first); soProducerIndex(p + 2); } else { final int capacity = buffer.length(); producerBuffer = newBuffer; pi = calcWrappedOffset(p, m); soElement(newBuffer, pi + 1, second);// StoreStore soElement(newBuffer, pi, first); soNext(buffer, newBuffer); soElement(buffer, pi, HAS_NEXT); // new buffer is visible after element is soProducerIndex(p + 2);// this ensures correctness on 32bit platforms
public final boolean offer(final T 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 AtomicReferenceArray<Object> buffer = producerBuffer; final long index = lpProducerIndex(); final int mask = producerMask; final int 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) int 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; } } }
@SuppressWarnings("unchecked") private T newBufferPoll(AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) { consumerBuffer = nextBuffer; final int offsetInNew = calcWrappedOffset(index, mask); final T n = (T) lvElement(nextBuffer, offsetInNew);// LoadLoad if (null != n) { soElement(nextBuffer, offsetInNew, null);// StoreStore soConsumerIndex(index + 1);// this ensures correctness on 32bit platforms } return n; }
public final boolean isEmpty() { return lvProducerIndex() == lvConsumerIndex(); }
@SuppressWarnings("unchecked") private T newBufferPeek(AtomicReferenceArray<Object> nextBuffer, final long index, final int mask) { consumerBuffer = nextBuffer; final int offsetInNew = calcWrappedOffset(index, mask); return (T) lvElement(nextBuffer, offsetInNew);// LoadLoad }