Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.netty.buffer.PoolChunk Maven / Gradle / Ivy
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.buffer;
final class PoolChunk {
private static final int ST_UNUSED = 0;
private static final int ST_BRANCH = 1;
private static final int ST_ALLOCATED = 2;
private static final int ST_ALLOCATED_SUBPAGE = ST_ALLOCATED | 1;
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
final PoolArena arena;
final T memory;
final boolean unpooled;
private final int[] memoryMap;
private final PoolSubpage[] subpages;
/** Used to determine if the requested capacity is equal to or greater than pageSize. */
private final int subpageOverflowMask;
private final int pageSize;
private final int pageShifts;
private final int chunkSize;
private final int maxSubpageAllocs;
private long random = (System.nanoTime() ^ multiplier) & mask;
private int freeBytes;
PoolChunkList parent;
PoolChunk prev;
PoolChunk next;
// TODO: Test if adding padding helps under contention
//private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
PoolChunk(PoolArena arena, T memory, int pageSize, int maxOrder, int pageShifts, int chunkSize) {
unpooled = false;
this.arena = arena;
this.memory = memory;
this.pageSize = pageSize;
this.pageShifts = pageShifts;
this.chunkSize = chunkSize;
subpageOverflowMask = ~(pageSize - 1);
freeBytes = chunkSize;
int chunkSizeInPages = chunkSize >>> pageShifts;
maxSubpageAllocs = 1 << maxOrder;
// Generate the memory map.
memoryMap = new int[maxSubpageAllocs << 1];
int memoryMapIndex = 1;
for (int i = 0; i <= maxOrder; i ++) {
int runSizeInPages = chunkSizeInPages >>> i;
for (int j = 0; j < chunkSizeInPages; j += runSizeInPages) {
//noinspection PointlessBitwiseExpression
memoryMap[memoryMapIndex ++] = j << 17 | runSizeInPages << 2 | ST_UNUSED;
}
}
subpages = newSubpageArray(maxSubpageAllocs);
}
/** Creates a special chunk that is not pooled. */
PoolChunk(PoolArena arena, T memory, int size) {
unpooled = true;
this.arena = arena;
this.memory = memory;
memoryMap = null;
subpages = null;
subpageOverflowMask = 0;
pageSize = 0;
pageShifts = 0;
chunkSize = size;
maxSubpageAllocs = 0;
}
@SuppressWarnings("unchecked")
private PoolSubpage[] newSubpageArray(int size) {
return new PoolSubpage[size];
}
int usage() {
if (freeBytes == 0) {
return 100;
}
int freePercentage = (int) (freeBytes * 100L / chunkSize);
if (freePercentage == 0) {
return 99;
}
return 100 - freePercentage;
}
long allocate(int normCapacity) {
int firstVal = memoryMap[1];
if ((normCapacity & subpageOverflowMask) != 0) { // >= pageSize
return allocateRun(normCapacity, 1, firstVal);
} else {
return allocateSubpage(normCapacity, 1, firstVal);
}
}
private long allocateRun(int normCapacity, int curIdx, int val) {
for (;;) {
if ((val & ST_ALLOCATED) != 0) { // state == ST_ALLOCATED || state == ST_ALLOCATED_SUBPAGE
return -1;
}
if ((val & ST_BRANCH) != 0) { // state == ST_BRANCH
int nextIdx = curIdx << 1 ^ nextRandom();
long res = allocateRun(normCapacity, nextIdx, memoryMap[nextIdx]);
if (res > 0) {
return res;
}
curIdx = nextIdx ^ 1;
val = memoryMap[curIdx];
continue;
}
// state == ST_UNUSED
return allocateRunSimple(normCapacity, curIdx, val);
}
}
private long allocateRunSimple(int normCapacity, int curIdx, int val) {
int runLength = runLength(val);
if (normCapacity > runLength) {
return -1;
}
for (;;) {
if (normCapacity == runLength) {
// Found the run that fits.
// Note that capacity has been normalized already, so we don't need to deal with
// the values that are not power of 2.
memoryMap[curIdx] = val & ~3 | ST_ALLOCATED;
freeBytes -= runLength;
return curIdx;
}
int nextIdx = curIdx << 1 ^ nextRandom();
int unusedIdx = nextIdx ^ 1;
memoryMap[curIdx] = val & ~3 | ST_BRANCH;
//noinspection PointlessBitwiseExpression
memoryMap[unusedIdx] = memoryMap[unusedIdx] & ~3 | ST_UNUSED;
runLength >>>= 1;
curIdx = nextIdx;
val = memoryMap[curIdx];
}
}
private long allocateSubpage(int normCapacity, int curIdx, int val) {
int state = val & 3;
if (state == ST_BRANCH) {
int nextIdx = curIdx << 1 ^ nextRandom();
long res = branchSubpage(normCapacity, nextIdx);
if (res > 0) {
return res;
}
return branchSubpage(normCapacity, nextIdx ^ 1);
}
if (state == ST_UNUSED) {
return allocateSubpageSimple(normCapacity, curIdx, val);
}
if (state == ST_ALLOCATED_SUBPAGE) {
PoolSubpage subpage = subpages[subpageIdx(curIdx)];
int elemSize = subpage.elemSize;
if (normCapacity != elemSize) {
return -1;
}
return subpage.allocate();
}
return -1;
}
private long allocateSubpageSimple(int normCapacity, int curIdx, int val) {
int runLength = runLength(val);
for (;;) {
if (runLength == pageSize) {
memoryMap[curIdx] = val & ~3 | ST_ALLOCATED_SUBPAGE;
freeBytes -= runLength;
int subpageIdx = subpageIdx(curIdx);
PoolSubpage subpage = subpages[subpageIdx];
if (subpage == null) {
subpage = new PoolSubpage(this, curIdx, runOffset(val), pageSize, normCapacity);
subpages[subpageIdx] = subpage;
} else {
subpage.init(normCapacity);
}
return subpage.allocate();
}
int nextIdx = curIdx << 1 ^ nextRandom();
int unusedIdx = nextIdx ^ 1;
memoryMap[curIdx] = val & ~3 | ST_BRANCH;
//noinspection PointlessBitwiseExpression
memoryMap[unusedIdx] = memoryMap[unusedIdx] & ~3 | ST_UNUSED;
runLength >>>= 1;
curIdx = nextIdx;
val = memoryMap[curIdx];
}
}
private long branchSubpage(int normCapacity, int nextIdx) {
int nextVal = memoryMap[nextIdx];
if ((nextVal & 3) != ST_ALLOCATED) {
return allocateSubpage(normCapacity, nextIdx, nextVal);
}
return -1;
}
void free(long handle) {
int memoryMapIdx = (int) handle;
int bitmapIdx = (int) (handle >>> 32);
int val = memoryMap[memoryMapIdx];
int state = val & 3;
if (state == ST_ALLOCATED_SUBPAGE) {
assert bitmapIdx != 0;
PoolSubpage subpage = subpages[subpageIdx(memoryMapIdx)];
assert subpage != null && subpage.doNotDestroy;
if (subpage.free(bitmapIdx & 0x3FFFFFFF)) {
return;
}
} else {
assert state == ST_ALLOCATED : "state: " + state;
assert bitmapIdx == 0;
}
freeBytes += runLength(val);
for (;;) {
//noinspection PointlessBitwiseExpression
memoryMap[memoryMapIdx] = val & ~3 | ST_UNUSED;
if (memoryMapIdx == 1) {
assert freeBytes == chunkSize;
return;
}
if ((memoryMap[siblingIdx(memoryMapIdx)] & 3) != ST_UNUSED) {
break;
}
memoryMapIdx = parentIdx(memoryMapIdx);
val = memoryMap[memoryMapIdx];
}
}
void initBuf(PooledByteBuf buf, long handle, int reqCapacity) {
int memoryMapIdx = (int) handle;
int bitmapIdx = (int) (handle >>> 32);
if (bitmapIdx == 0) {
int val = memoryMap[memoryMapIdx];
assert (val & 3) == ST_ALLOCATED : String.valueOf(val & 3);
buf.init(this, handle, runOffset(val), reqCapacity, runLength(val));
} else {
initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
}
}
void initBufWithSubpage(PooledByteBuf buf, long handle, int reqCapacity) {
initBufWithSubpage(buf, handle, (int) (handle >>> 32), reqCapacity);
}
private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx, int reqCapacity) {
assert bitmapIdx != 0;
int memoryMapIdx = (int) handle;
int val = memoryMap[memoryMapIdx];
assert (val & 3) == ST_ALLOCATED_SUBPAGE;
PoolSubpage subpage = subpages[subpageIdx(memoryMapIdx)];
assert subpage.doNotDestroy;
assert reqCapacity <= subpage.elemSize;
buf.init(
this, handle,
runOffset(val) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize, reqCapacity, subpage.elemSize);
}
private static int parentIdx(int memoryMapIdx) {
return memoryMapIdx >>> 1;
}
private static int siblingIdx(int memoryMapIdx) {
return memoryMapIdx ^ 1;
}
private int runLength(int val) {
return (val >>> 2 & 0x7FFF) << pageShifts;
}
private int runOffset(int val) {
return val >>> 17 << pageShifts;
}
private int subpageIdx(int memoryMapIdx) {
return memoryMapIdx - maxSubpageAllocs;
}
private int nextRandom() {
random = random * multiplier + addend & mask;
return (int) (random >>> 47) & 1;
}
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("Chunk(");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(": ");
buf.append(usage());
buf.append("%, ");
buf.append(chunkSize - freeBytes);
buf.append('/');
buf.append(chunkSize);
buf.append(')');
return buf.toString();
}
}