/** * Create a new multiple producer RingBuffer with the specified wait strategy. * * @param <E> Class of the event stored in the ring buffer. * @param factory used to create the events within the ring buffer. * @param bufferSize number of elements to create within the ring buffer. * @param waitStrategy used to determine how to wait for new elements to become available. * @return a constructed ring buffer. * @throws IllegalArgumentException if bufferSize is less than 1 or not a power of 2 * @see MultiProducerSequencer */ public static <E> RingBuffer<E> createMultiProducer( EventFactory<E> factory, int bufferSize, WaitStrategy waitStrategy) { MultiProducerSequencer sequencer = new MultiProducerSequencer(bufferSize, waitStrategy); return new RingBuffer<E>(factory, sequencer); }
/** * The below methods work on the availableBuffer flag. * <p> * The prime reason is to avoid a shared sequence object between publisher threads. * (Keeping single pointers tracking start and end would require coordination * between the threads). * <p> * -- Firstly we have the constraint that the delta between the cursor and minimum * gating sequence will never be larger than the buffer size (the code in * next/tryNext in the Sequence takes care of that). * -- Given that; take the sequence value and mask off the lower portion of the * sequence as the index into the buffer (indexMask). (aka modulo operator) * -- The upper portion of the sequence becomes the value to check for availability. * ie: it tells us how many times around the ring buffer we've been (aka division) * -- Because we can't wrap without the gating sequences moving forward (i.e. the * minimum gating sequence is effectively our last available position in the * buffer), when we have new data and successfully claimed a slot we can simply * write over the top. */ private void setAvailable(final long sequence) { setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence)); }
/** * @see Sequencer#remainingCapacity() */ @Override public long remainingCapacity() { long consumed = Util.getMinimumSequence(gatingSequences, cursor.get()); long produced = cursor.get(); return getBufferSize() - (produced - consumed); }
/** * @see Sequencer#isAvailable(long) */ @Override public boolean isAvailable(long sequence) { int index = calculateIndex(sequence); int flag = calculateAvailabilityFlag(sequence); long bufferAddress = (index * SCALE) + BASE; return UNSAFE.getIntVolatile(availableBuffer, bufferAddress) == flag; }
/** * @see Sequencer#hasAvailableCapacity(int) */ @Override public boolean hasAvailableCapacity(final int requiredCapacity) { return hasAvailableCapacity(gatingSequences, requiredCapacity, cursor.get()); }
/** * @see Sequencer#publish(long) */ @Override public void publish(final long sequence) { setAvailable(sequence); waitStrategy.signalAllWhenBlocking(); }
@Override public long getHighestPublishedSequence(long lowerBound, long availableSequence) { for (long sequence = lowerBound; sequence <= availableSequence; sequence++) { if (!isAvailable(sequence)) { return sequence - 1; } } return availableSequence; }
private void initialiseAvailableBuffer() { for (int i = availableBuffer.length - 1; i != 0; i--) { setAvailableBufferValue(i, -1); } setAvailableBufferValue(0, -1); }
/** * @see Sequencer#next() */ @Override public long next() { return next(1); }
/** * Construct a Sequencer with the selected wait strategy and buffer size. * * @param bufferSize the size of the buffer that this will sequence over. * @param waitStrategy for those waiting on sequences. */ public MultiProducerSequencer(int bufferSize, final WaitStrategy waitStrategy) { super(bufferSize, waitStrategy); availableBuffer = new int[bufferSize]; indexMask = bufferSize - 1; indexShift = Util.log2(bufferSize); initialiseAvailableBuffer(); }
/** * @see Sequencer#isAvailable(long) */ @Override public boolean isAvailable(long sequence) { int index = calculateIndex(sequence); int flag = calculateAvailabilityFlag(sequence); long bufferAddress = (index * SCALE) + BASE; return UNSAFE.getIntVolatile(availableBuffer, bufferAddress) == flag; }
/** * @see Sequencer#tryNext(int) */ @Override public long tryNext(int n) throws InsufficientCapacityException { if (n < 1) { throw new IllegalArgumentException("n must be > 0"); } long current; long next; do { current = cursor.get(); next = current + n; if (!hasAvailableCapacity(gatingSequences, n, current)) { throw InsufficientCapacityException.INSTANCE; } } while (!cursor.compareAndSet(current, next)); return next; }
/** * @see Sequencer#publish(long, long) */ @Override public void publish(long lo, long hi) { for (long l = lo; l <= hi; l++) { setAvailable(l); } waitStrategy.signalAllWhenBlocking(); }
@Override public long getHighestPublishedSequence(long lowerBound, long availableSequence) { for (long sequence = lowerBound; sequence <= availableSequence; sequence++) { if (!isAvailable(sequence)) { return sequence - 1; } } return availableSequence; }
private void initialiseAvailableBuffer() { for (int i = availableBuffer.length - 1; i != 0; i--) { setAvailableBufferValue(i, -1); } setAvailableBufferValue(0, -1); }
/** * @see Sequencer#next() */ @Override public long next() { return next(1); }
/** * Construct a Sequencer with the selected wait strategy and buffer size. * * @param bufferSize the size of the buffer that this will sequence over. * @param waitStrategy for those waiting on sequences. */ public MultiProducerSequencer(int bufferSize, final WaitStrategy waitStrategy) { super(bufferSize, waitStrategy); availableBuffer = new int[bufferSize]; indexMask = bufferSize - 1; indexShift = Util.log2(bufferSize); initialiseAvailableBuffer(); }
/** * The below methods work on the availableBuffer flag. * <p> * The prime reason is to avoid a shared sequence object between publisher threads. * (Keeping single pointers tracking start and end would require coordination * between the threads). * <p> * -- Firstly we have the constraint that the delta between the cursor and minimum * gating sequence will never be larger than the buffer size (the code in * next/tryNext in the Sequence takes care of that). * -- Given that; take the sequence value and mask off the lower portion of the * sequence as the index into the buffer (indexMask). (aka modulo operator) * -- The upper portion of the sequence becomes the value to check for availability. * ie: it tells us how many times around the ring buffer we've been (aka division) * -- Because we can't wrap without the gating sequences moving forward (i.e. the * minimum gating sequence is effectively our last available position in the * buffer), when we have new data and successfully claimed a slot we can simply * write over the top. */ private void setAvailable(final long sequence) { setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence)); }
/** * @see Sequencer#isAvailable(long) */ @Override public boolean isAvailable(long sequence) { int index = calculateIndex(sequence); int flag = calculateAvailabilityFlag(sequence); long bufferAddress = (index * SCALE) + BASE; return UNSAFE.getIntVolatile(availableBuffer, bufferAddress) == flag; }
/** * @see Sequencer#hasAvailableCapacity(int) */ @Override public boolean hasAvailableCapacity(final int requiredCapacity) { return hasAvailableCapacity(gatingSequences, requiredCapacity, cursor.get()); }