2012-12-05 22:11:48 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2015-05-13 17:15:06 +02:00
|
|
|
final class PoolSubpage<T> implements PoolSubpageMetric {
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
final PoolChunk<T> chunk;
|
2014-05-30 03:45:28 +02:00
|
|
|
private final int memoryMapIdx;
|
|
|
|
private final int runOffset;
|
|
|
|
private final int pageSize;
|
|
|
|
private final long[] bitmap;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
2013-03-05 15:55:41 +01:00
|
|
|
PoolSubpage<T> prev;
|
|
|
|
PoolSubpage<T> next;
|
|
|
|
|
2012-12-05 22:11:48 +01:00
|
|
|
boolean doNotDestroy;
|
|
|
|
int elemSize;
|
2014-05-30 03:45:28 +02:00
|
|
|
private int maxNumElems;
|
|
|
|
private int bitmapLength;
|
|
|
|
private int nextAvail;
|
|
|
|
private int numAvail;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
// TODO: Test if adding padding helps under contention
|
|
|
|
//private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
|
|
|
|
2013-03-05 15:55:41 +01:00
|
|
|
/** Special constructor that creates a linked list head */
|
|
|
|
PoolSubpage(int pageSize) {
|
|
|
|
chunk = null;
|
|
|
|
memoryMapIdx = -1;
|
|
|
|
runOffset = -1;
|
|
|
|
elemSize = -1;
|
|
|
|
this.pageSize = pageSize;
|
|
|
|
bitmap = null;
|
|
|
|
}
|
|
|
|
|
2012-12-05 22:11:48 +01:00
|
|
|
PoolSubpage(PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) {
|
|
|
|
this.chunk = chunk;
|
|
|
|
this.memoryMapIdx = memoryMapIdx;
|
|
|
|
this.runOffset = runOffset;
|
|
|
|
this.pageSize = pageSize;
|
|
|
|
bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64
|
|
|
|
init(elemSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void init(int elemSize) {
|
|
|
|
doNotDestroy = true;
|
|
|
|
this.elemSize = elemSize;
|
2013-03-05 15:55:41 +01:00
|
|
|
if (elemSize != 0) {
|
|
|
|
maxNumElems = numAvail = pageSize / elemSize;
|
|
|
|
nextAvail = 0;
|
|
|
|
bitmapLength = maxNumElems >>> 6;
|
|
|
|
if ((maxNumElems & 63) != 0) {
|
|
|
|
bitmapLength ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < bitmapLength; i ++) {
|
|
|
|
bitmap[i] = 0;
|
|
|
|
}
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 15:55:41 +01:00
|
|
|
addToPool();
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the bitmap index of the subpage allocation.
|
|
|
|
*/
|
|
|
|
long allocate() {
|
|
|
|
if (elemSize == 0) {
|
|
|
|
return toHandle(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numAvail == 0 || !doNotDestroy) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:45:28 +02:00
|
|
|
final int bitmapIdx = getNextAvail();
|
2012-12-05 22:11:48 +01:00
|
|
|
int q = bitmapIdx >>> 6;
|
|
|
|
int r = bitmapIdx & 63;
|
|
|
|
assert (bitmap[q] >>> r & 1) == 0;
|
|
|
|
bitmap[q] |= 1L << r;
|
|
|
|
|
|
|
|
if (-- numAvail == 0) {
|
2013-03-05 15:55:41 +01:00
|
|
|
removeFromPool();
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return toHandle(bitmapIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {@code true} if this subpage is in use.
|
|
|
|
* {@code false} if this subpage is not used by its chunk and thus it's OK to be released.
|
|
|
|
*/
|
|
|
|
boolean free(int bitmapIdx) {
|
2013-06-21 04:15:24 +02:00
|
|
|
|
2012-12-05 22:11:48 +01:00
|
|
|
if (elemSize == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int q = bitmapIdx >>> 6;
|
|
|
|
int r = bitmapIdx & 63;
|
|
|
|
assert (bitmap[q] >>> r & 1) != 0;
|
|
|
|
bitmap[q] ^= 1L << r;
|
|
|
|
|
2014-05-30 03:45:28 +02:00
|
|
|
setNextAvail(bitmapIdx);
|
|
|
|
|
2012-12-05 22:11:48 +01:00
|
|
|
if (numAvail ++ == 0) {
|
2013-03-05 15:55:41 +01:00
|
|
|
addToPool();
|
2012-12-05 22:11:48 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-21 04:15:24 +02:00
|
|
|
if (numAvail != maxNumElems) {
|
2012-12-05 22:11:48 +01:00
|
|
|
return true;
|
|
|
|
} else {
|
2013-06-21 04:15:24 +02:00
|
|
|
// Subpage not in use (numAvail == maxNumElems)
|
|
|
|
if (prev == next) {
|
|
|
|
// Do not remove if this subpage is the only one left in the pool.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove this subpage from the pool if there are other subpages left in the pool.
|
2012-12-05 22:11:48 +01:00
|
|
|
doNotDestroy = false;
|
2013-03-05 15:55:41 +01:00
|
|
|
removeFromPool();
|
2012-12-05 22:11:48 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 15:55:41 +01:00
|
|
|
private void addToPool() {
|
|
|
|
PoolSubpage<T> head = chunk.arena.findSubpagePoolHead(elemSize);
|
|
|
|
assert prev == null && next == null;
|
|
|
|
prev = head;
|
|
|
|
next = head.next;
|
|
|
|
next.prev = this;
|
|
|
|
head.next = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void removeFromPool() {
|
|
|
|
assert prev != null && next != null;
|
|
|
|
prev.next = next;
|
|
|
|
next.prev = prev;
|
|
|
|
next = null;
|
|
|
|
prev = null;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:45:28 +02:00
|
|
|
private void setNextAvail(int bitmapIdx) {
|
|
|
|
nextAvail = bitmapIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getNextAvail() {
|
|
|
|
int nextAvail = this.nextAvail;
|
|
|
|
if (nextAvail >= 0) {
|
|
|
|
this.nextAvail = -1;
|
|
|
|
return nextAvail;
|
|
|
|
}
|
|
|
|
return findNextAvail();
|
|
|
|
}
|
|
|
|
|
|
|
|
private int findNextAvail() {
|
|
|
|
final long[] bitmap = this.bitmap;
|
|
|
|
final int bitmapLength = this.bitmapLength;
|
2012-12-05 22:11:48 +01:00
|
|
|
for (int i = 0; i < bitmapLength; i ++) {
|
|
|
|
long bits = bitmap[i];
|
|
|
|
if (~bits != 0) {
|
2014-05-30 03:45:28 +02:00
|
|
|
return findNextAvail0(i, bits);
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:45:28 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-12-05 22:11:48 +01:00
|
|
|
|
2014-05-30 03:45:28 +02:00
|
|
|
private int findNextAvail0(int i, long bits) {
|
|
|
|
final int maxNumElems = this.maxNumElems;
|
|
|
|
final int baseVal = i << 6;
|
|
|
|
|
|
|
|
for (int j = 0; j < 64; j ++) {
|
|
|
|
if ((bits & 1) == 0) {
|
|
|
|
int val = baseVal | j;
|
|
|
|
if (val < maxNumElems) {
|
|
|
|
return val;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bits >>>= 1;
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
2014-05-30 03:45:28 +02:00
|
|
|
return -1;
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private long toHandle(int bitmapIdx) {
|
|
|
|
return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:15:06 +02:00
|
|
|
@Override
|
2012-12-05 22:11:48 +01:00
|
|
|
public String toString() {
|
|
|
|
if (!doNotDestroy) {
|
|
|
|
return "(" + memoryMapIdx + ": not in use)";
|
|
|
|
}
|
|
|
|
|
|
|
|
return String.valueOf('(') + memoryMapIdx + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
|
|
|
|
", offset: " + runOffset + ", length: " + pageSize + ", elemSize: " + elemSize + ')';
|
|
|
|
}
|
2015-05-13 17:15:06 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int maxNumElements() {
|
|
|
|
return maxNumElems;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int numAvailable() {
|
|
|
|
return numAvail;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int elementSize() {
|
|
|
|
return elemSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int pageSize() {
|
|
|
|
return pageSize;
|
|
|
|
}
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|