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;
|
||||
|
||||
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,57 +64,68 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
|
||||
|
||||
@Override
|
||||
public final int refCnt() {
|
||||
return 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() {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt <= 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
public MessageBuf<T> retain() {
|
||||
for (;;) {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt == 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
if (refCnt == Integer.MAX_VALUE) {
|
||||
throw new IllegalBufferAccessException("refCnt overflow");
|
||||
}
|
||||
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (refCnt == Integer.MAX_VALUE) {
|
||||
throw new IllegalBufferAccessException("refCnt overflow");
|
||||
}
|
||||
|
||||
this.refCnt = refCnt + 1;
|
||||
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)");
|
||||
}
|
||||
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt <= 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
for (;;) {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt == 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
if (refCnt > Integer.MAX_VALUE - increment) {
|
||||
throw new IllegalBufferAccessException("refCnt overflow");
|
||||
}
|
||||
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (refCnt > Integer.MAX_VALUE - increment) {
|
||||
throw new IllegalBufferAccessException("refCnt overflow");
|
||||
}
|
||||
|
||||
this.refCnt = refCnt + increment;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean release() {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt <= 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
for (;;) {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt == 0) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
|
||||
this.refCnt = refCnt --;
|
||||
if (refCnt == 0) {
|
||||
deallocate();
|
||||
return true;
|
||||
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
|
||||
if (refCnt == 1) {
|
||||
deallocate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,18 +134,20 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
|
||||
throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)");
|
||||
}
|
||||
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt < decrement) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
for (;;) {
|
||||
int refCnt = this.refCnt;
|
||||
if (refCnt < decrement) {
|
||||
throw new IllegalBufferAccessException();
|
||||
}
|
||||
|
||||
this.refCnt = refCnt -= decrement;
|
||||
if (refCnt == 0) {
|
||||
deallocate();
|
||||
return true;
|
||||
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
|
||||
if (refCnt == decrement) {
|
||||
deallocate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void deallocate();
|
||||
|
@ -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,12 +24,30 @@ 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() {
|
||||
return 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
|
||||
|
@ -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,8 +49,13 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int refCnt() {
|
||||
return 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user