@Override
    public int drain(Consumer<E> c, int limit) {
        if (null == c)
            throw new IllegalArgumentException("c is null");
        if (limit < 0)
            throw new IllegalArgumentException("limit is negative: " + limit);
        if (limit == 0)
            return 0;
        final AtomicLongArray sBuffer = sequenceBuffer;
        final int mask = this.mask;
        final AtomicReferenceArray<E> buffer = this.buffer;
        final int maxLookAheadStep = Math.min(this.lookAheadStep, limit);
        int consumed = 0;
        while (consumed < limit) {
            final int remaining = limit - consumed;
            final int lookAheadStep = Math.min(remaining, maxLookAheadStep);
            final long cIndex = lvConsumerIndex();
            final long lookAheadIndex = cIndex + lookAheadStep - 1;
            final int lookAheadSeqOffset = calcCircularLongElementOffset(lookAheadIndex, mask);
            final long lookAheadSeq = lvLongElement(sBuffer, lookAheadSeqOffset);
            final long expectedLookAheadSeq = lookAheadIndex + 1;
            if (lookAheadSeq == expectedLookAheadSeq && casConsumerIndex(cIndex, expectedLookAheadSeq)) {
                for (int i = 0; i < lookAheadStep; i++) {
                    final long index = cIndex + i;
                    final int seqOffset = calcCircularLongElementOffset(index, mask);
                    final int offset = calcCircularRefElementOffset(index, mask);
                    final long expectedSeq = index + 1;
                    while (lvLongElement(sBuffer, seqOffset) != expectedSeq) {
                    }
                    final E e = lpRefElement(buffer, offset);
                    spRefElement(buffer, offset, null);
                    soLongElement(sBuffer, seqOffset, index + mask + 1);
                    c.accept(e);
                }
                consumed += lookAheadStep;
            } else {
                if (lookAheadSeq < expectedLookAheadSeq) {
                    if (notAvailable(cIndex, mask, sBuffer, cIndex + 1)) {
                        return consumed;
                    }
                }
                return consumed + drainOneByOne(c, remaining);
            }
        }
        return limit;
    }
{@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