Relaxed memory access constraint of ReferenceCounted.refCnt() for potentially better performance / More precise reference counting for MessageBuf
This commit is contained in:
parent
6ac9b17ddd
commit
12f1d96914
@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package io.netty.buffer;
|
package io.netty.buffer;
|
||||||
|
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
import java.util.AbstractQueue;
|
import java.util.AbstractQueue;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for {@link MessageBuf} implementations.
|
* Abstract base class for {@link MessageBuf} implementations.
|
||||||
@ -25,8 +28,27 @@ import java.util.Collection;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements MessageBuf<T> {
|
public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements MessageBuf<T> {
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static final AtomicIntegerFieldUpdater<AbstractMessageBuf> refCntUpdater =
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(AbstractMessageBuf.class, "refCnt");
|
||||||
|
|
||||||
|
private static final long REFCNT_FIELD_OFFSET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
long refCntFieldOffset = -1;
|
||||||
|
try {
|
||||||
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
|
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractMessageBuf.class.getDeclaredField("refCnt"));
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) { }
|
||||||
|
|
||||||
|
REFCNT_FIELD_OFFSET = refCntFieldOffset;
|
||||||
|
}
|
||||||
|
|
||||||
private final int maxCapacity;
|
private final int maxCapacity;
|
||||||
private int refCnt = 1;
|
|
||||||
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
|
private volatile int refCnt = 1;
|
||||||
|
|
||||||
protected AbstractMessageBuf(int maxCapacity) {
|
protected AbstractMessageBuf(int maxCapacity) {
|
||||||
if (maxCapacity < 0) {
|
if (maxCapacity < 0) {
|
||||||
@ -42,58 +64,69 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int refCnt() {
|
public final int refCnt() {
|
||||||
|
if (REFCNT_FIELD_OFFSET >= 0) {
|
||||||
|
// Try to do non-volatile read for performance.
|
||||||
|
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
|
||||||
|
} else {
|
||||||
return refCnt;
|
return refCnt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final MessageBuf<T> retain() {
|
public MessageBuf<T> retain() {
|
||||||
|
for (;;) {
|
||||||
int refCnt = this.refCnt;
|
int refCnt = this.refCnt;
|
||||||
if (refCnt <= 0) {
|
if (refCnt == 0) {
|
||||||
throw new IllegalBufferAccessException();
|
throw new IllegalBufferAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refCnt == Integer.MAX_VALUE) {
|
if (refCnt == Integer.MAX_VALUE) {
|
||||||
throw new IllegalBufferAccessException("refCnt overflow");
|
throw new IllegalBufferAccessException("refCnt overflow");
|
||||||
}
|
}
|
||||||
|
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
|
||||||
this.refCnt = refCnt + 1;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final MessageBuf<T> retain(int increment) {
|
public MessageBuf<T> retain(int increment) {
|
||||||
if (increment <= 0) {
|
if (increment <= 0) {
|
||||||
throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
|
throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
int refCnt = this.refCnt;
|
int refCnt = this.refCnt;
|
||||||
if (refCnt <= 0) {
|
if (refCnt == 0) {
|
||||||
throw new IllegalBufferAccessException();
|
throw new IllegalBufferAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refCnt > Integer.MAX_VALUE - increment) {
|
if (refCnt > Integer.MAX_VALUE - increment) {
|
||||||
throw new IllegalBufferAccessException("refCnt overflow");
|
throw new IllegalBufferAccessException("refCnt overflow");
|
||||||
}
|
}
|
||||||
|
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
|
||||||
this.refCnt = refCnt + increment;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean release() {
|
public final boolean release() {
|
||||||
|
for (;;) {
|
||||||
int refCnt = this.refCnt;
|
int refCnt = this.refCnt;
|
||||||
if (refCnt <= 0) {
|
if (refCnt == 0) {
|
||||||
throw new IllegalBufferAccessException();
|
throw new IllegalBufferAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refCnt = refCnt --;
|
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
|
||||||
if (refCnt == 0) {
|
if (refCnt == 1) {
|
||||||
deallocate();
|
deallocate();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean release(int decrement) {
|
public final boolean release(int decrement) {
|
||||||
@ -101,19 +134,21 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
|
|||||||
throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)");
|
throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
int refCnt = this.refCnt;
|
int refCnt = this.refCnt;
|
||||||
if (refCnt < decrement) {
|
if (refCnt < decrement) {
|
||||||
throw new IllegalBufferAccessException();
|
throw new IllegalBufferAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refCnt = refCnt -= decrement;
|
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
|
||||||
if (refCnt == 0) {
|
if (refCnt == decrement) {
|
||||||
deallocate();
|
deallocate();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void deallocate();
|
protected abstract void deallocate();
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.buffer;
|
package io.netty.buffer;
|
||||||
|
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
public abstract class AbstractReferenceCounted implements ReferenceCounted {
|
public abstract class AbstractReferenceCounted implements ReferenceCounted {
|
||||||
@ -22,13 +24,31 @@ public abstract class AbstractReferenceCounted implements ReferenceCounted {
|
|||||||
private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater =
|
private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater =
|
||||||
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt");
|
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt");
|
||||||
|
|
||||||
|
private static final long REFCNT_FIELD_OFFSET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
long refCntFieldOffset = -1;
|
||||||
|
try {
|
||||||
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
|
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractReferenceCounted.class.getDeclaredField("refCnt"));
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) { }
|
||||||
|
|
||||||
|
REFCNT_FIELD_OFFSET = refCntFieldOffset;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("FieldMayBeFinal")
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
private volatile int refCnt = 1;
|
private volatile int refCnt = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int refCnt() {
|
public final int refCnt() {
|
||||||
|
if (REFCNT_FIELD_OFFSET >= 0) {
|
||||||
|
// Try to do non-volatile read for performance.
|
||||||
|
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
|
||||||
|
} else {
|
||||||
return refCnt;
|
return refCnt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReferenceCounted retain() {
|
public ReferenceCounted retain() {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package io.netty.buffer;
|
package io.netty.buffer;
|
||||||
|
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,6 +28,19 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
|
|||||||
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
|
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
|
||||||
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
|
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
|
||||||
|
|
||||||
|
private static final long REFCNT_FIELD_OFFSET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
long refCntFieldOffset = -1;
|
||||||
|
try {
|
||||||
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
|
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) { }
|
||||||
|
|
||||||
|
REFCNT_FIELD_OFFSET = refCntFieldOffset;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("FieldMayBeFinal")
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
private volatile int refCnt = 1;
|
private volatile int refCnt = 1;
|
||||||
|
|
||||||
@ -34,9 +49,14 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int refCnt() {
|
public final int refCnt() {
|
||||||
|
if (REFCNT_FIELD_OFFSET >= 0) {
|
||||||
|
// Try to do non-volatile read for performance.
|
||||||
|
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
|
||||||
|
} else {
|
||||||
return refCnt;
|
return refCnt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf retain() {
|
public ByteBuf retain() {
|
||||||
|
@ -217,6 +217,10 @@ public final class PlatformDependent {
|
|||||||
return PlatformDependent0.getObject(object, fieldOffset);
|
return PlatformDependent0.getObject(object, fieldOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getInt(Object object, long fieldOffset) {
|
||||||
|
return PlatformDependent0.getInt(object, fieldOffset);
|
||||||
|
}
|
||||||
|
|
||||||
public static long objectFieldOffset(Field field) {
|
public static long objectFieldOffset(Field field) {
|
||||||
return PlatformDependent0.objectFieldOffset(field);
|
return PlatformDependent0.objectFieldOffset(field);
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,10 @@ final class PlatformDependent0 {
|
|||||||
return UNSAFE.getObject(object, fieldOffset);
|
return UNSAFE.getObject(object, fieldOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int getInt(Object object, long fieldOffset) {
|
||||||
|
return UNSAFE.getInt(object, fieldOffset);
|
||||||
|
}
|
||||||
|
|
||||||
private static long getLong(Object object, long fieldOffset) {
|
private static long getLong(Object object, long fieldOffset) {
|
||||||
return UNSAFE.getLong(object, fieldOffset);
|
return UNSAFE.getLong(object, fieldOffset);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user