package io.mycat.net;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author wuzh
*/
public final class BufferPool {
// this value not changed ,isLocalCacheThread use it
public static final String LOCAL_BUF_THREAD_PREX = "$_";
private final ThreadLocalBufferPool localBufferPool;
private static final Logger LOGGER = LoggerFactory
.getLogger(BufferPool.class);
private final int chunkSize;
private final int conReadBuferChunk;
private final ConcurrentLinkedQueue<ByteBuffer> items = new ConcurrentLinkedQueue<ByteBuffer>();
/**
* 只用于Connection读取Socket事件,每个Connection一个ByteBuffer(Direct),
* 此ByteBufer通常应该能容纳2-N个 应用消息的报文长度,
* 对于超出的报文长度,则由BufferPool单独份分配临时的堆内ByteBuffer
*/
private final ConcurrentLinkedQueue<ByteBuffer> conReadBuferQueue = new ConcurrentLinkedQueue<ByteBuffer>();
private long sharedOptsCount;
private int newCreated;
private final long threadLocalCount;
private final long capactiy;
public BufferPool(long bufferSize, int chunkSize, int conReadBuferChunk,
int threadLocalPercent) {
this.chunkSize = chunkSize;
this.conReadBuferChunk = conReadBuferChunk;
long size = bufferSize / chunkSize;
size = (bufferSize % chunkSize == 0) ? size : size + 1;
this.capactiy = size;
threadLocalCount = threadLocalPercent * capactiy / 100;
for (int i = 0; i < capactiy; i++) {
items.offer(createDirectBuffer(chunkSize));
}
localBufferPool = new ThreadLocalBufferPool(threadLocalCount);
}
private static final boolean isLocalCacheThread() {
final String thname = Thread.currentThread().getName();
return (thname.length() < LOCAL_BUF_THREAD_PREX.length()) ? false
: (thname.charAt(0) == '$' && thname.charAt(1) == '_');
}
public int getConReadBuferChunk() {
return conReadBuferChunk;
}
public int getChunkSize() {
return chunkSize;
}
public long getSharedOptsCount() {
return sharedOptsCount;
}
public long size() {
return this.items.size();
}
public long capacity() {
return capactiy + newCreated;
}
public ByteBuffer allocateConReadBuffer() {
ByteBuffer result = conReadBuferQueue.poll();
if (result != null) {
return result;
} else {
return createDirectBuffer(conReadBuferChunk);
}
}
public BufferArray allocateArray() {
return new BufferArray(this);
}
public ByteBuffer allocate() {
ByteBuffer node = null;
if (isLocalCacheThread()) {
// allocate from threadlocal
node = localBufferPool.get().poll();
if (node != null) {
return node;
}
}
node = items.poll();
if (node == null) {
newCreated++;
node = this.createDirectBuffer(chunkSize);
}
return node;
}
private boolean checkValidBuffer(ByteBuffer buffer) {
// 拒绝回收null和容量大于chunkSize的缓存
if (buffer == null || !buffer.isDirect()) {
return false;
} else if (buffer.capacity() != chunkSize) {
LOGGER.warn("cant' recycle a buffer not equals my pool chunksize "
+ chunkSize + " he is " + buffer.capacity());
throw new RuntimeException("bad size");
// return false;
}
buffer.clear();
return true;
}
public void recycleConReadBuffer(ByteBuffer buffer) {
if (buffer == null || !buffer.isDirect()) {
return;
} else if (buffer.capacity() != conReadBuferChunk) {
LOGGER.warn("cant' recycle a buffer not equals my pool con read chunksize "
+ buffer.capacity());
} else {
buffer.clear();
this.conReadBuferQueue.add(buffer);
}
}
public void recycle(ByteBuffer buffer) {
if (!checkValidBuffer(buffer)) {
return;
}
if (isLocalCacheThread()) {
BufferQueue localQueue = localBufferPool.get();
if (localQueue.snapshotSize() < threadLocalCount) {
localQueue.put(buffer);
} else {
// recyle 3/4 thread local buffer
items.addAll(localQueue.removeItems(threadLocalCount * 3 / 4));
items.offer(buffer);
sharedOptsCount++;
}
} else {
sharedOptsCount++;
items.offer(buffer);
}
}
public boolean testIfDuplicate(ByteBuffer buffer) {
for (ByteBuffer exists : items) {
if (exists == buffer) {
return true;
}
}
return false;
}
private ByteBuffer createDirectBuffer(int size) {
// for performance
return ByteBuffer.allocateDirect(size);
}
public ByteBuffer allocate(int size) {
if (size <= this.chunkSize) {
return allocate();
} else {
LOGGER.warn("allocate buffer size large than default chunksize:"
+ this.chunkSize + " he want " + size);
throw new RuntimeException("execuddd");
// return createTempBuffer(size);
}
}
public static void main(String[] args) {
BufferPool pool = new BufferPool(1024 * 5, 1024, 1024 * 3, 2);
long i = pool.capacity();
ArrayList<ByteBuffer> all = new ArrayList<ByteBuffer>();
for (int j = 0; j <= i; j++) {
all.add(pool.allocate());
}
for (ByteBuffer buf : all) {
pool.recycle(buf);
}
System.out.println(pool.size());
}
}