@Override
    public E poll() {
        // local load of field to avoid repeated loads after volatile reads
        final AtomicLongArray sBuffer = sequenceBuffer;
        final int mask = this.mask;
        long cIndex;
        long seq;
        int seqOffset;
        long expectedSeq;
        // start with bogus value, hope we don't need it
        long pIndex = -1;
        do {
            cIndex = lvConsumerIndex();
            seqOffset = calcCircularLongElementOffset(cIndex, mask);
            seq = lvLongElement(sBuffer, seqOffset);
            expectedSeq = cIndex + 1;
            if (seq < expectedSeq) {
                // slot has not been moved by producer
                if (// test against cached pIndex
                cIndex >= pIndex && // update pIndex if we must
                cIndex == (pIndex = lvProducerIndex())) {
                    // strict empty check, this ensures [Queue.poll() == null iff isEmpty()]
                    return null;
                } else {
                    // trip another go around
                    seq = expectedSeq + 1;
                }
            }
        } while (// another consumer beat us to it
        seq > expectedSeq || // failed the CAS
        !casConsumerIndex(cIndex, cIndex + 1));
        final int offset = calcCircularRefElementOffset(cIndex, mask);
        final E e = lpRefElement(buffer, offset);
        spRefElement(buffer, offset, null);
        // i.e. seq += capacity
        soLongElement(sBuffer, seqOffset, cIndex + mask + 1);
        return e;
    }
{@inheritDoc}
<p>
Because return null indicates queue is empty we cannot simply rely on next element visibility for poll
and must test producer index when next element is not visible.
jctools-core/src/main/java/org/jctools/queues/atomic/MpmcAtomicArrayQueue.java