package org.webpieces.data.api; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Feel free to completely override this class but basically as ChannelManager feeds * you ByteBuffers, they are 100% ALWAYS created from this pool. You can then release * the ByteBuffer to be re-used (rather than having to garbage collect request memory * over and over taking that performance hit). Just call * * BufferCreationPool.releaseBuffer(buffer); and ChannelManager will clear that buffer * and re-use it for the next set of data coming in. * * We could behind the BufferPool have N BufferCreationPools as well to further reduce * contention if any existed since this class is shared between async http parser, * async ssl engine, clients, channelmanager writes involving all threads from * SessionExecutor and all threads that call write() from the client as well...TBD * * @author dhiller */ public class BufferCreationPool implements BufferPool { private static final Logger log = LoggerFactory.getLogger(BufferCreationPool.class); public static final int DEFAULT_MAX_BUFFER_SIZE = 16921; //a rough counter...doesn't need to be too accurate.. private AtomicInteger counter = new AtomicInteger(); private ConcurrentLinkedQueue<ByteBuffer> freePackets = new ConcurrentLinkedQueue<ByteBuffer>(); private boolean isDirect; private int bufferSize; private int poolSize; public BufferCreationPool() { this(false, DEFAULT_MAX_BUFFER_SIZE, 1000); } public BufferCreationPool(boolean isDirect, int bufferSize, int poolSize) { this.isDirect = isDirect; this.bufferSize = bufferSize; this.poolSize = poolSize; } public ByteBuffer nextBuffer(int minSize) { if(bufferSize < minSize) { log.error("minSize="+minSize+" requests is larger than the buffer size provided by this pool="+bufferSize+". You should reconfigure this "); return createBuffer(minSize); } ByteBuffer buffer = freePackets.poll(); if(buffer == null) { buffer = createBuffer(bufferSize); } else { counter.decrementAndGet(); } return buffer; } public ByteBuffer createWithDataWrapper(DataWrapper data) { ByteBuffer byteBuffer = nextBuffer(data.getReadableSize()); byteBuffer.put(data.createByteArray()); byteBuffer.flip(); return byteBuffer; } private ByteBuffer createBuffer(int size2) { ByteBuffer buffer; if(isDirect) buffer = ByteBuffer.allocateDirect(size2); else buffer = ByteBuffer.allocate(size2); return buffer; } @Override public void releaseBuffer(ByteBuffer buffer) { if(buffer.remaining() != 0) { throw new IllegalArgumentException("You need to consume all data from your buffer (or " + "call buffer.position(buffer.limit)) to simulate consuming it though this is ill advised as you" + "should be reading all your data from your buffer before releasing it"); } if(counter.incrementAndGet() > poolSize) return; //we discard more than N buffers as we don't want to take up too much memory else if(buffer.capacity() < bufferSize) { return; //discard buffers that are released and are smaller } buffer.clear(); freePackets.add(buffer); } }