/* * 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 org.jctools.channels.mpsc; import static org.jctools.util.UnsafeAccess.UNSAFE; import static org.jctools.util.UnsafeDirectByteBuffer.allocateAlignedByteBuffer; import java.nio.ByteBuffer; import org.jctools.channels.OffHeapFixedMessageSizeRingBuffer; import org.jctools.util.JvmInfo; import org.jctools.util.Pow2; /** * Channel protocol: * - Fixed message size * - 'null' indicator in message preceding byte (potentially use same for type mapping in future) * - Use Vyukov MPMC like algorithm relying on indicator to support in place detection of next element existence */ public class MpscOffHeapFixedSizeRingBuffer extends OffHeapFixedMessageSizeRingBuffer { public MpscOffHeapFixedSizeRingBuffer(final int capacity, final int messageSize, int referenceMessageSize) { this(allocateAlignedByteBuffer(getRequiredBufferSize(capacity, messageSize), JvmInfo.CACHE_LINE_SIZE), Pow2.roundToPowerOfTwo(capacity), true, true, true, messageSize, createReferenceArray(capacity, referenceMessageSize), referenceMessageSize); } /** * This is to be used for an IPC queue with the direct buffer used being a memory mapped file. * * @param buff * @param capacity */ protected MpscOffHeapFixedSizeRingBuffer(final ByteBuffer buff, final int capacity, final boolean isProducer, final boolean isConsumer, final boolean initialize, final int messageSize, final Object[] references, final int referenceMessageSize) { super(buff, capacity, isProducer, isConsumer, initialize, messageSize, references, referenceMessageSize); } @Override protected final long writeAcquire() { long producerIndex; long offset; do { producerIndex = lvProducerIndex(); // LoadLoad offset = offsetForIndex(producerIndex); // This is a bug! we need to replace with a solution a-la Vyukuv MPMC or similar slot 'phase' indicator if (!this.isReadReleased(offset)) { // It is possible that due to another producer passing us we are seeing that producer completed message, // if that is the case then we must retry. if (producerIndex != lvProducerIndex()) { continue;// go around again } return EOF; } } while (!casProducerIndex(producerIndex, producerIndex + 1)); // return offset for current producer index return offset; } @Override protected final void writeRelease(long offset) { writeReleaseState(offset); } @Override protected final void writeRelease(long offset, int callTypeId) { assert callTypeId != 0; UNSAFE.putOrderedInt(null, offset, callTypeId); } @Override protected final long readAcquire() { final long currentHead = lpConsumerIndex(); final long offset = offsetForIndex(currentHead); if (isReadReleased(offset)) { return EOF; } soConsumerIndex(currentHead + 1); // StoreStore return offset; } @Override protected final void readRelease(long offset) { readReleaseState(offset); } private boolean casProducerIndex(final long expected, long update) { return UNSAFE.compareAndSwapLong(null, producerIndexAddress, expected, update); } }