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.maxOrder = maxOrder;
|
||||||
this.pageShifts = pageShifts;
|
this.pageShifts = pageShifts;
|
||||||
this.chunkSize = chunkSize;
|
this.chunkSize = chunkSize;
|
||||||
this.directMemoryCacheAlignment = cacheAlignment;
|
directMemoryCacheAlignment = cacheAlignment;
|
||||||
this.directMemoryCacheAlignmentMask = cacheAlignment - 1;
|
directMemoryCacheAlignmentMask = cacheAlignment - 1;
|
||||||
subpageOverflowMask = ~(pageSize - 1);
|
subpageOverflowMask = ~(pageSize - 1);
|
||||||
tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
|
tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
|
||||||
for (int i = 0; i < tinySubpagePools.length; i ++) {
|
for (int i = 0; i < tinySubpagePools.length; i ++) {
|
||||||
@ -103,12 +103,12 @@ abstract class PoolArena<T> implements PoolArenaMetric {
|
|||||||
smallSubpagePools[i] = newSubpagePoolHead(pageSize);
|
smallSubpagePools[i] = newSubpagePoolHead(pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
q100 = new PoolChunkList<T>(null, 100, Integer.MAX_VALUE, chunkSize);
|
q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, chunkSize);
|
||||||
q075 = new PoolChunkList<T>(q100, 75, 100, chunkSize);
|
q075 = new PoolChunkList<T>(this, q100, 75, 100, chunkSize);
|
||||||
q050 = new PoolChunkList<T>(q075, 50, 100, chunkSize);
|
q050 = new PoolChunkList<T>(this, q075, 50, 100, chunkSize);
|
||||||
q025 = new PoolChunkList<T>(q050, 25, 75, chunkSize);
|
q025 = new PoolChunkList<T>(this, q050, 25, 75, chunkSize);
|
||||||
q000 = new PoolChunkList<T>(q025, 1, 50, chunkSize);
|
q000 = new PoolChunkList<T>(this, q025, 1, 50, chunkSize);
|
||||||
qInit = new PoolChunkList<T>(q000, Integer.MIN_VALUE, 25, chunkSize);
|
qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, chunkSize);
|
||||||
|
|
||||||
q100.prevList(q075);
|
q100.prevList(q075);
|
||||||
q075.prevList(q050);
|
q075.prevList(q050);
|
||||||
|
@ -192,7 +192,14 @@ final class PoolChunk<T> implements PoolChunkMetric {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int usage() {
|
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) {
|
if (freeBytes == 0) {
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
@ -447,22 +454,29 @@ final class PoolChunk<T> implements PoolChunkMetric {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int freeBytes() {
|
public int freeBytes() {
|
||||||
return freeBytes;
|
synchronized (arena) {
|
||||||
|
return freeBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
final int freeBytes;
|
||||||
|
synchronized (arena) {
|
||||||
|
freeBytes = this.freeBytes;
|
||||||
|
}
|
||||||
|
|
||||||
return new StringBuilder()
|
return new StringBuilder()
|
||||||
.append("Chunk(")
|
.append("Chunk(")
|
||||||
.append(Integer.toHexString(System.identityHashCode(this)))
|
.append(Integer.toHexString(System.identityHashCode(this)))
|
||||||
.append(": ")
|
.append(": ")
|
||||||
.append(usage())
|
.append(usage(freeBytes))
|
||||||
.append("%, ")
|
.append("%, ")
|
||||||
.append(chunkSize - freeBytes)
|
.append(chunkSize - freeBytes)
|
||||||
.append('/')
|
.append('/')
|
||||||
.append(chunkSize)
|
.append(chunkSize)
|
||||||
.append(')')
|
.append(')')
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy() {
|
void destroy() {
|
||||||
|
@ -27,6 +27,7 @@ import static java.lang.Math.*;
|
|||||||
|
|
||||||
final class PoolChunkList<T> implements PoolChunkListMetric {
|
final class PoolChunkList<T> implements PoolChunkListMetric {
|
||||||
private static final Iterator<PoolChunkMetric> EMPTY_METRICS = Collections.<PoolChunkMetric>emptyList().iterator();
|
private static final Iterator<PoolChunkMetric> EMPTY_METRICS = Collections.<PoolChunkMetric>emptyList().iterator();
|
||||||
|
private final PoolArena<T> arena;
|
||||||
private final PoolChunkList<T> nextList;
|
private final PoolChunkList<T> nextList;
|
||||||
private final int minUsage;
|
private final int minUsage;
|
||||||
private final int maxUsage;
|
private final int maxUsage;
|
||||||
@ -39,8 +40,9 @@ final class PoolChunkList<T> implements PoolChunkListMetric {
|
|||||||
// TODO: Test if adding padding helps under contention
|
// TODO: Test if adding padding helps under contention
|
||||||
//private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
//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;
|
assert minUsage <= maxUsage;
|
||||||
|
this.arena = arena;
|
||||||
this.nextList = nextList;
|
this.nextList = nextList;
|
||||||
this.minUsage = minUsage;
|
this.minUsage = minUsage;
|
||||||
this.maxUsage = maxUsage;
|
this.maxUsage = maxUsage;
|
||||||
@ -190,36 +192,39 @@ final class PoolChunkList<T> implements PoolChunkListMetric {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<PoolChunkMetric> iterator() {
|
public Iterator<PoolChunkMetric> iterator() {
|
||||||
if (head == null) {
|
synchronized (arena) {
|
||||||
return EMPTY_METRICS;
|
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;
|
|
||||||
}
|
}
|
||||||
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (head == null) {
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
for (PoolChunk<T> cur = head;;) {
|
synchronized (arena) {
|
||||||
buf.append(cur);
|
if (head == null) {
|
||||||
cur = cur.next;
|
return "none";
|
||||||
if (cur == null) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
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();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,27 +200,50 @@ final class PoolSubpage<T> implements PoolSubpageMetric {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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) {
|
if (!doNotDestroy) {
|
||||||
return "(" + memoryMapIdx + ": not in use)";
|
return "(" + memoryMapIdx + ": not in use)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.valueOf('(') + memoryMapIdx + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
|
return "(" + memoryMapIdx + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
|
||||||
", offset: " + runOffset + ", length: " + pageSize + ", elemSize: " + elemSize + ')';
|
", offset: " + runOffset + ", length: " + pageSize + ", elemSize: " + elemSize + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int maxNumElements() {
|
public int maxNumElements() {
|
||||||
return maxNumElems;
|
synchronized (chunk.arena) {
|
||||||
|
return maxNumElems;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int numAvailable() {
|
public int numAvailable() {
|
||||||
return numAvail;
|
synchronized (chunk.arena) {
|
||||||
|
return numAvail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int elementSize() {
|
public int elementSize() {
|
||||||
return elemSize;
|
synchronized (chunk.arena) {
|
||||||
|
return elemSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user