Relaxed memory access constraint of ReferenceCounted.refCnt() for potentially better performance / More precise reference counting for MessageBuf

This commit is contained in:
Trustin Lee 2013-03-08 10:32:20 +09:00
parent 6ac9b17ddd
commit 12f1d96914
5 changed files with 129 additions and 46 deletions

View File

@ -16,8 +16,11 @@
package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* 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> {
@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 int refCnt = 1;
@SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1;
protected AbstractMessageBuf(int maxCapacity) {
if (maxCapacity < 0) {
@ -42,58 +64,69 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
@Override
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;
}
}
@Override
public final MessageBuf<T> retain() {
public MessageBuf<T> retain() {
for (;;) {
int refCnt = this.refCnt;
if (refCnt <= 0) {
if (refCnt == 0) {
throw new IllegalBufferAccessException();
}
if (refCnt == Integer.MAX_VALUE) {
throw new IllegalBufferAccessException("refCnt overflow");
}
this.refCnt = refCnt + 1;
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
break;
}
}
return this;
}
@Override
public final MessageBuf<T> retain(int increment) {
public MessageBuf<T> retain(int increment) {
if (increment <= 0) {
throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
}
for (;;) {
int refCnt = this.refCnt;
if (refCnt <= 0) {
if (refCnt == 0) {
throw new IllegalBufferAccessException();
}
if (refCnt > Integer.MAX_VALUE - increment) {
throw new IllegalBufferAccessException("refCnt overflow");
}
this.refCnt = refCnt + increment;
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
break;
}
}
return this;
}
@Override
public final boolean release() {
for (;;) {
int refCnt = this.refCnt;
if (refCnt <= 0) {
if (refCnt == 0) {
throw new IllegalBufferAccessException();
}
this.refCnt = refCnt --;
if (refCnt == 0) {
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
if (refCnt == 1) {
deallocate();
return true;
}
return false;
}
}
}
@Override
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)");
}
for (;;) {
int refCnt = this.refCnt;
if (refCnt < decrement) {
throw new IllegalBufferAccessException();
}
this.refCnt = refCnt -= decrement;
if (refCnt == 0) {
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
if (refCnt == decrement) {
deallocate();
return true;
}
return false;
}
}
}
protected abstract void deallocate();

View File

@ -15,6 +15,8 @@
*/
package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public abstract class AbstractReferenceCounted implements ReferenceCounted {
@ -22,13 +24,31 @@ public abstract class AbstractReferenceCounted implements ReferenceCounted {
private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater =
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")
private volatile int refCnt = 1;
@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;
}
}
@Override
public ReferenceCounted retain() {

View File

@ -16,6 +16,8 @@
package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
@ -26,6 +28,19 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
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")
private volatile int refCnt = 1;
@ -34,9 +49,14 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
}
@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;
}
}
@Override
public ByteBuf retain() {

View File

@ -217,6 +217,10 @@ public final class PlatformDependent {
return PlatformDependent0.getObject(object, fieldOffset);
}
public static int getInt(Object object, long fieldOffset) {
return PlatformDependent0.getInt(object, fieldOffset);
}
public static long objectFieldOffset(Field field) {
return PlatformDependent0.objectFieldOffset(field);
}

View File

@ -149,6 +149,10 @@ final class PlatformDependent0 {
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) {
return UNSAFE.getLong(object, fieldOffset);
}