/*
* 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.spsc;
import org.jctools.channels.Channel;
import org.jctools.channels.ChannelConsumer;
import org.jctools.channels.ChannelProducer;
import org.jctools.channels.ChannelReceiver;
import org.jctools.channels.mapping.Mapper;
import org.jctools.util.Pow2;
import org.jctools.util.Template;
import java.nio.ByteBuffer;
import static org.jctools.channels.spsc.SpscOffHeapFixedSizeRingBuffer.getLookaheadStep;
import static org.jctools.channels.spsc.SpscOffHeapFixedSizeRingBuffer.getRequiredBufferSize;
public final class SpscChannel<E> implements Channel<E> {
// TODO; property configuration
private static final boolean debugEnabled = false;
private final int elementSize;
private final Mapper<E> mapper;
private final ByteBuffer buffer;
private final int maximumCapacity;
private final int requestedCapacity;
private final SpscChannelProducer<E> producer;
/**
* This is to be used for an IPC queue with the direct buffer used being a memory mapped file.
*
* @param buffer
* @param requestedCapacity
*/
// TODO: take an initialize parameter
public SpscChannel(final ByteBuffer buffer, final int requestedCapacity, final Class<E> type) {
this.requestedCapacity = requestedCapacity;
this.maximumCapacity = getMaximumCapacity(requestedCapacity);
this.buffer = buffer;
mapper = new Mapper<E>(type, debugEnabled);
elementSize = mapper.getSizeInBytes();
checkSufficientCapacity();
checkByteBuffer();
producer = newProducer(type, buffer, maximumCapacity, elementSize);
}
private int getMaximumCapacity(int requestedCapacity) {
return Pow2.roundToPowerOfTwo(requestedCapacity + getLookaheadStep(requestedCapacity));
}
private void checkByteBuffer() {
if (!buffer.isDirect()) {
throw new IllegalArgumentException("Channels only work with direct or memory mapped buffers");
}
}
private void checkSufficientCapacity() {
final int requiredCapacityInBytes = getRequiredBufferSize(maximumCapacity, elementSize);
if (buffer.capacity() < requiredCapacityInBytes) {
throw new IllegalArgumentException("Failed to meet required maximumCapacity in bytes: "
+ requiredCapacityInBytes);
}
}
public ChannelConsumer consumer(ChannelReceiver<E> receiver) {
return newConsumer(buffer, maximumCapacity, elementSize, receiver);
}
public ChannelProducer<E> producer() {
return producer;
}
public int size() {
return producer.size();
}
public int maximumCapacity() {
return maximumCapacity;
}
@Override
public int requestedCapacity() {
return requestedCapacity;
}
public boolean isEmpty() {
return size() == 0;
}
@SuppressWarnings("unchecked")
private SpscChannelProducer<E> newProducer(final Class<E> type, final Object... args) {
return mapper.newFlyweight(SpscChannelProducer.class, "ChannelProducerTemplate.java",
Template.fromFile(Channel.class, "ChannelProducerTemplate.java"), args);
}
@SuppressWarnings("unchecked")
private SpscChannelConsumer<E> newConsumer(Object... args) {
return mapper.newFlyweight(SpscChannelConsumer.class, "ChannelConsumerTemplate.java",
Template.fromFile(Channel.class, "ChannelConsumerTemplate.java"), args);
}
}