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:
Norman Maurer 2017-02-27 21:09:07 +01:00
parent 4734ef61a5
commit a7fe6c0153
4 changed files with 89 additions and 47 deletions

View File

@ -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);

View File

@ -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() {

View File

@ -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();
}

View File

@ -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