Metrics exposed by PooledByteBufAllocator needs to be correctly synchronized
Motivation: As we may access the metrics exposed of PooledByteBufAllocator from another thread then the allocations happen we need to ensure we synchronize on the PoolArena to ensure correct visibility. Modifications: Synchronize on the PoolArena to ensure correct visibility. Result: Fix multi-thread issues on the metrics
This commit is contained in:
parent
ed370dcebb
commit
d54ed19e2a
@ -89,8 +89,8 @@ abstract class PoolArena<T> implements PoolArenaMetric {
|
||||
this.maxOrder = maxOrder;
|
||||
this.pageShifts = pageShifts;
|
||||
this.chunkSize = chunkSize;
|
||||
this.directMemoryCacheAlignment = cacheAlignment;
|
||||
this.directMemoryCacheAlignmentMask = cacheAlignment - 1;
|
||||
directMemoryCacheAlignment = cacheAlignment;
|
||||
directMemoryCacheAlignmentMask = cacheAlignment - 1;
|
||||
subpageOverflowMask = ~(pageSize - 1);
|
||||
tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
|
||||
for (int i = 0; i < tinySubpagePools.length; i ++) {
|
||||
@ -103,12 +103,12 @@ abstract class PoolArena<T> implements PoolArenaMetric {
|
||||
smallSubpagePools[i] = newSubpagePoolHead(pageSize);
|
||||
}
|
||||
|
||||
q100 = new PoolChunkList<T>(null, 100, Integer.MAX_VALUE, chunkSize);
|
||||
q075 = new PoolChunkList<T>(q100, 75, 100, chunkSize);
|
||||
q050 = new PoolChunkList<T>(q075, 50, 100, chunkSize);
|
||||
q025 = new PoolChunkList<T>(q050, 25, 75, chunkSize);
|
||||
q000 = new PoolChunkList<T>(q025, 1, 50, chunkSize);
|
||||
qInit = new PoolChunkList<T>(q000, Integer.MIN_VALUE, 25, chunkSize);
|
||||
q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, chunkSize);
|
||||
q075 = new PoolChunkList<T>(this, q100, 75, 100, chunkSize);
|
||||
q050 = new PoolChunkList<T>(this, q075, 50, 100, chunkSize);
|
||||
q025 = new PoolChunkList<T>(this, q050, 25, 75, chunkSize);
|
||||
q000 = new PoolChunkList<T>(this, q025, 1, 50, chunkSize);
|
||||
qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, chunkSize);
|
||||
|
||||
q100.prevList(q075);
|
||||
q075.prevList(q050);
|
||||
|
@ -192,7 +192,14 @@ final class PoolChunk<T> implements PoolChunkMetric {
|
||||
|
||||
@Override
|
||||
public int usage() {
|
||||
final int freeBytes = this.freeBytes;
|
||||
final int freeBytes;
|
||||
synchronized (arena) {
|
||||
freeBytes = this.freeBytes;
|
||||
}
|
||||
return usage(freeBytes);
|
||||
}
|
||||
|
||||
private int usage(int freeBytes) {
|
||||
if (freeBytes == 0) {
|
||||
return 100;
|
||||
}
|
||||
@ -447,22 +454,29 @@ final class PoolChunk<T> implements PoolChunkMetric {
|
||||
|
||||
@Override
|
||||
public int freeBytes() {
|
||||
return freeBytes;
|
||||
synchronized (arena) {
|
||||
return freeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final int freeBytes;
|
||||
synchronized (arena) {
|
||||
freeBytes = this.freeBytes;
|
||||
}
|
||||
|
||||
return new StringBuilder()
|
||||
.append("Chunk(")
|
||||
.append(Integer.toHexString(System.identityHashCode(this)))
|
||||
.append(": ")
|
||||
.append(usage())
|
||||
.append("%, ")
|
||||
.append(chunkSize - freeBytes)
|
||||
.append('/')
|
||||
.append(chunkSize)
|
||||
.append(')')
|
||||
.toString();
|
||||
.append("Chunk(")
|
||||
.append(Integer.toHexString(System.identityHashCode(this)))
|
||||
.append(": ")
|
||||
.append(usage(freeBytes))
|
||||
.append("%, ")
|
||||
.append(chunkSize - freeBytes)
|
||||
.append('/')
|
||||
.append(chunkSize)
|
||||
.append(')')
|
||||
.toString();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
|
@ -27,6 +27,7 @@ import static java.lang.Math.*;
|
||||
|
||||
final class PoolChunkList<T> implements PoolChunkListMetric {
|
||||
private static final Iterator<PoolChunkMetric> EMPTY_METRICS = Collections.<PoolChunkMetric>emptyList().iterator();
|
||||
private final PoolArena<T> arena;
|
||||
private final PoolChunkList<T> nextList;
|
||||
private final int minUsage;
|
||||
private final int maxUsage;
|
||||
@ -39,8 +40,9 @@ final class PoolChunkList<T> implements PoolChunkListMetric {
|
||||
// TODO: Test if adding padding helps under contention
|
||||
//private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
||||
|
||||
PoolChunkList(PoolChunkList<T> nextList, int minUsage, int maxUsage, int chunkSize) {
|
||||
PoolChunkList(PoolArena<T> arena, PoolChunkList<T> nextList, int minUsage, int maxUsage, int chunkSize) {
|
||||
assert minUsage <= maxUsage;
|
||||
this.arena = arena;
|
||||
this.nextList = nextList;
|
||||
this.minUsage = minUsage;
|
||||
this.maxUsage = maxUsage;
|
||||
@ -190,36 +192,39 @@ final class PoolChunkList<T> implements PoolChunkListMetric {
|
||||
|
||||
@Override
|
||||
public Iterator<PoolChunkMetric> iterator() {
|
||||
if (head == null) {
|
||||
return EMPTY_METRICS;
|
||||
}
|
||||
List<PoolChunkMetric> metrics = new ArrayList<PoolChunkMetric>();
|
||||
for (PoolChunk<T> cur = head;;) {
|
||||
metrics.add(cur);
|
||||
cur = cur.next;
|
||||
if (cur == null) {
|
||||
break;
|
||||
synchronized (arena) {
|
||||
if (head == null) {
|
||||
return EMPTY_METRICS;
|
||||
}
|
||||
List<PoolChunkMetric> metrics = new ArrayList<PoolChunkMetric>();
|
||||
for (PoolChunk<T> cur = head;;) {
|
||||
metrics.add(cur);
|
||||
cur = cur.next;
|
||||
if (cur == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return metrics.iterator();
|
||||
}
|
||||
return metrics.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (head == null) {
|
||||
return "none";
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (PoolChunk<T> cur = head;;) {
|
||||
buf.append(cur);
|
||||
cur = cur.next;
|
||||
if (cur == null) {
|
||||
break;
|
||||
synchronized (arena) {
|
||||
if (head == null) {
|
||||
return "none";
|
||||
}
|
||||
buf.append(StringUtil.NEWLINE);
|
||||
}
|
||||
|
||||
for (PoolChunk<T> cur = head;;) {
|
||||
buf.append(cur);
|
||||
cur = cur.next;
|
||||
if (cur == null) {
|
||||
break;
|
||||
}
|
||||
buf.append(StringUtil.NEWLINE);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
@ -200,27 +200,50 @@ final class PoolSubpage<T> implements PoolSubpageMetric {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final boolean doNotDestroy;
|
||||
final int maxNumElems;
|
||||
final int numAvail;
|
||||
final int elemSize;
|
||||
synchronized (chunk.arena) {
|
||||
if (!this.doNotDestroy) {
|
||||
doNotDestroy = false;
|
||||
// Not used for creating the String.
|
||||
maxNumElems = numAvail = elemSize = -1;
|
||||
} else {
|
||||
doNotDestroy = true;
|
||||
maxNumElems = this.maxNumElems;
|
||||
numAvail = this.numAvail;
|
||||
elemSize = this.elemSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doNotDestroy) {
|
||||
return "(" + memoryMapIdx + ": not in use)";
|
||||
}
|
||||
|
||||
return String.valueOf('(') + memoryMapIdx + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
|
||||
", offset: " + runOffset + ", length: " + pageSize + ", elemSize: " + elemSize + ')';
|
||||
return "(" + memoryMapIdx + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
|
||||
", offset: " + runOffset + ", length: " + pageSize + ", elemSize: " + elemSize + ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxNumElements() {
|
||||
return maxNumElems;
|
||||
synchronized (chunk.arena) {
|
||||
return maxNumElems;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numAvailable() {
|
||||
return numAvail;
|
||||
synchronized (chunk.arena) {
|
||||
return numAvail;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int elementSize() {
|
||||
return elemSize;
|
||||
synchronized (chunk.arena) {
|
||||
return elemSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user