/*
 * Copyright 2011 LMAX Ltd.
 *
 * 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 com.lmax.disruptor;

import static com.lmax.disruptor.util.Util.getMinimumSequence;

import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.locks.LockSupport;

import com.lmax.disruptor.util.MutableLong;
import com.lmax.disruptor.util.PaddedAtomicLong;

/**
 * Strategy to be used when there are multiple publisher threads claiming sequences.
 *
 * This strategy is reasonably forgiving when the multiple publisher threads are highly contended or working in an
 * environment where there is insufficient CPUs to handle multiple publisher threads.  It requires 2 CAS operations
 * for a single publisher, compared to the {@link MultiThreadedLowContentionClaimStrategy} strategy which needs only a single
 * CAS and a lazySet per publication.
 */
public final class MultiThreadedClaimStrategy
    implements ClaimStrategy
{
    private static final int RETRIES = 1000;

    private final int bufferSize;
    private final PaddedAtomicLong claimSequence = new PaddedAtomicLong(Sequencer.INITIAL_CURSOR_VALUE);
    private final AtomicLongArray pendingPublication;
    private final int pendingMask;

    private final ThreadLocal<MutableLong> minGatingSequenceThreadLocal = new ThreadLocal<MutableLong>()
    {
        @Override
        protected MutableLong initialValue()
        {
            return new MutableLong(Sequencer.INITIAL_CURSOR_VALUE);
        }
    };

    /**
     * Construct a new multi-threaded publisher {@link ClaimStrategy} for a given buffer size.
     *
     * @param bufferSize for the underlying data structure.
     * @param pendingBufferSize number of item that can be pending for serialisation
     */
    public MultiThreadedClaimStrategy(final int bufferSize, final int pendingBufferSize)
    {
        if (Integer.bitCount(bufferSize) != 1)
        {
            throw new IllegalArgumentException("bufferSize must be a power of 2, was: " + bufferSize);
        }
        
        if (Integer.bitCount(pendingBufferSize) != 1)
        {
            throw new IllegalArgumentException("pendingBufferSize must be a power of 2, was: " + pendingBufferSize);
        }

        this.bufferSize = bufferSize;
        this.pendingPublication = new AtomicLongArray(pendingBufferSize);
        this.pendingMask = pendingBufferSize - 1;
    }

    /**
     * Construct a new multi-threaded publisher {@link ClaimStrategy} for a given buffer size.
     *
     * @param bufferSize for the underlying data structure.
     */
    public MultiThreadedClaimStrategy(final int bufferSize)
    {
        this(bufferSize, 1024);
    }

    @Override
    public int getBufferSize()
    {
        return bufferSize;
    }

    @Override
    public long getSequence()
    {
        return claimSequence.get();
    }

    @Override
    public boolean hasAvailableCapacity(final int availableCapacity, final Sequence[] dependentSequences)
    {
        final long wrapPoint = (claimSequence.get() + availableCapacity) - bufferSize;
        final MutableLong minGatingSequence = minGatingSequenceThreadLocal.get();
        if (wrapPoint > minGatingSequence.get())
        {
            long minSequence = getMinimumSequence(dependentSequences);
            minGatingSequence.set(minSequence);

            if (wrapPoint > minSequence)
            {
                return false;
            }
        }

        return true;
    }

    @Override
    public long incrementAndGet(final Sequence[] dependentSequences)
    {
        final MutableLong minGatingSequence = minGatingSequenceThreadLocal.get();
        waitForCapacity(dependentSequences, minGatingSequence);

        final long nextSequence = claimSequence.incrementAndGet();
        waitForFreeSlotAt(nextSequence, dependentSequences, minGatingSequence);

        return nextSequence;
    }

    @Override
    public long incrementAndGet(final int delta, final Sequence[] dependentSequences)
    {
        final long nextSequence = claimSequence.addAndGet(delta);
        waitForFreeSlotAt(nextSequence, dependentSequences, minGatingSequenceThreadLocal.get());

        return nextSequence;
    }

    @Override
    public void setSequence(final long sequence, final Sequence[] dependentSequences)
    {
        claimSequence.lazySet(sequence);
        waitForFreeSlotAt(sequence, dependentSequences, minGatingSequenceThreadLocal.get());
    }

    @Override
    public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)
    {
        int counter = RETRIES;
        while (sequence - cursor.get() > pendingPublication.length())
        {
            if (--counter == 0)
            {
                Thread.yield();
                counter = RETRIES;
            }
        }

        long expectedSequence = sequence - batchSize;
        for (long pendingSequence = expectedSequence + 1; pendingSequence <= sequence; pendingSequence++)
        {
            pendingPublication.set((int) pendingSequence & pendingMask, pendingSequence);
        }

        long cursorSequence = cursor.get();
        if (cursorSequence >= sequence)
        {
            return;
        }

        expectedSequence = Math.max(expectedSequence, cursorSequence);
        long nextSequence = expectedSequence + 1;
        while (cursor.compareAndSet(expectedSequence, nextSequence))
        {
            expectedSequence = nextSequence;
            nextSequence++;
            if (pendingPublication.get((int) nextSequence & pendingMask) != nextSequence)
            {
                break;
            }
        }
    }

    private void waitForCapacity(final Sequence[] dependentSequences, final MutableLong minGatingSequence)
    {
        final long wrapPoint = (claimSequence.get() + 1L) - bufferSize;
        if (wrapPoint > minGatingSequence.get())
        {
            long minSequence;
            while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))
            {
                LockSupport.parkNanos(1L);
            }

            minGatingSequence.set(minSequence);
        }
    }

    private void waitForFreeSlotAt(final long sequence, final Sequence[] dependentSequences, final MutableLong minGatingSequence)
    {
        final long wrapPoint = sequence - bufferSize;
        if (wrapPoint > minGatingSequence.get())
        {
            long minSequence;
            while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))
            {
                LockSupport.parkNanos(1L);
            }

            minGatingSequence.set(minSequence);
        }
    }
}

@@ -2344,187 +2344,8 @@
   %7B%0A
-        if (Integer.bitCount(bufferSize) != 1)%0A        %7B%0A            throw new IllegalArgumentException(%22bufferSize must be a power of 2, was: %22 + bufferSize);%0A        %7D%0A        %0A
     

Remove unnecessary check