Merge pull request #36 from netty/rc-thread-safety
Allow slices to obtain ownership when parent is closed
This commit is contained in:
commit
0272b1cf84
@ -45,6 +45,7 @@ class CleanerPooledDrop implements Drop<Buffer> {
|
||||
GatedCleanable c = (GatedCleanable) CLEANABLE.getAndSet(this, null);
|
||||
if (c != null) {
|
||||
c.clean();
|
||||
delegate.drop(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,24 +58,43 @@ class CleanerPooledDrop implements Drop<Buffer> {
|
||||
c.clean();
|
||||
}
|
||||
|
||||
var pool = this.pool;
|
||||
var mem = manager.unwrapRecoverableMemory(buf);
|
||||
var delegate = this.delegate;
|
||||
WeakReference<Buffer> ref = new WeakReference<>(buf);
|
||||
WeakReference<CleanerPooledDrop> ref = new WeakReference<>(this);
|
||||
AtomicBoolean gate = new AtomicBoolean(true);
|
||||
cleanable = new GatedCleanable(gate, CLEANER.register(this, () -> {
|
||||
if (gate.getAndSet(false)) {
|
||||
Buffer b = ref.get();
|
||||
if (b == null) {
|
||||
pool.recoverMemory(mem);
|
||||
} else {
|
||||
delegate.drop(b);
|
||||
}
|
||||
}
|
||||
}));
|
||||
cleanable = new GatedCleanable(gate, CLEANER.register(this, new CleanAction(pool, mem, ref, gate)));
|
||||
}
|
||||
|
||||
private static class GatedCleanable implements Cleanable {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CleanerPooledDrop(" + delegate + ')';
|
||||
}
|
||||
|
||||
private static final class CleanAction implements Runnable {
|
||||
private final SizeClassedMemoryPool pool;
|
||||
private final Object mem;
|
||||
private final WeakReference<CleanerPooledDrop> ref;
|
||||
private final AtomicBoolean gate;
|
||||
|
||||
private CleanAction(SizeClassedMemoryPool pool, Object mem, WeakReference<CleanerPooledDrop> ref,
|
||||
AtomicBoolean gate) {
|
||||
this.pool = pool;
|
||||
this.mem = mem;
|
||||
this.ref = ref;
|
||||
this.gate = gate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (gate.getAndSet(false)) {
|
||||
var monitored = ref.get();
|
||||
if (monitored == null) {
|
||||
pool.recoverMemory(mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GatedCleanable implements Cleanable {
|
||||
private final AtomicBoolean gate;
|
||||
private final Cleanable cleanable;
|
||||
|
||||
|
@ -31,16 +31,23 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
* non-composite copy of the buffer.
|
||||
*/
|
||||
private static final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
|
||||
private static final Drop<CompositeBuffer> COMPOSITE_DROP = buf -> {
|
||||
for (Buffer b : buf.bufs) {
|
||||
b.close();
|
||||
private static final Drop<CompositeBuffer> COMPOSITE_DROP = new Drop<CompositeBuffer>() {
|
||||
@Override
|
||||
public void drop(CompositeBuffer buf) {
|
||||
for (Buffer b : buf.bufs) {
|
||||
b.close();
|
||||
}
|
||||
buf.makeInaccessible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "COMPOSITE_DROP";
|
||||
}
|
||||
buf.makeInaccessible();
|
||||
};
|
||||
|
||||
private final BufferAllocator allocator;
|
||||
private final TornBufferAccessors tornBufAccessors;
|
||||
private final boolean isSendable;
|
||||
private Buffer[] bufs;
|
||||
private int[] offsets; // The offset, for the composite buffer, where each constituent buffer starts.
|
||||
private int capacity;
|
||||
@ -52,7 +59,7 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
private boolean readOnly;
|
||||
|
||||
CompositeBuffer(BufferAllocator allocator, Deref<Buffer>[] refs) {
|
||||
this(allocator, true, filterExternalBufs(refs), COMPOSITE_DROP, false);
|
||||
this(allocator, filterExternalBufs(refs), COMPOSITE_DROP, false);
|
||||
}
|
||||
|
||||
private static Buffer[] filterExternalBufs(Deref<Buffer>[] refs) {
|
||||
@ -106,11 +113,10 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
return Stream.of(buf);
|
||||
}
|
||||
|
||||
private CompositeBuffer(BufferAllocator allocator, boolean isSendable, Buffer[] bufs, Drop<CompositeBuffer> drop,
|
||||
private CompositeBuffer(BufferAllocator allocator, Buffer[] bufs, Drop<CompositeBuffer> drop,
|
||||
boolean acquireBufs) {
|
||||
super(drop);
|
||||
this.allocator = allocator;
|
||||
this.isSendable = isSendable;
|
||||
if (acquireBufs) {
|
||||
for (Buffer buf : bufs) {
|
||||
buf.acquire();
|
||||
@ -297,46 +303,31 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
offset + ", and length was " + length + '.');
|
||||
}
|
||||
Buffer choice = (Buffer) chooseBuffer(offset, 0);
|
||||
Buffer[] slices = null;
|
||||
acquire(); // Increase reference count of the original composite buffer.
|
||||
Drop<CompositeBuffer> drop = obj -> {
|
||||
close(); // Decrement the reference count of the original composite buffer.
|
||||
COMPOSITE_DROP.drop(obj);
|
||||
};
|
||||
Buffer[] slices;
|
||||
|
||||
try {
|
||||
if (length > 0) {
|
||||
slices = new Buffer[bufs.length];
|
||||
int off = subOffset;
|
||||
int cap = length;
|
||||
int i;
|
||||
for (i = searchOffsets(offset); cap > 0; i++) {
|
||||
var buf = bufs[i];
|
||||
int avail = buf.capacity() - off;
|
||||
slices[i] = buf.slice(off, Math.min(cap, avail));
|
||||
cap -= avail;
|
||||
off = 0;
|
||||
}
|
||||
slices = Arrays.copyOf(slices, i);
|
||||
} else {
|
||||
// Specialize for length == 0, since we must slice from at least one constituent buffer.
|
||||
slices = new Buffer[] { choice.slice(subOffset, 0) };
|
||||
}
|
||||
|
||||
return new CompositeBuffer(allocator, false, slices, drop, true);
|
||||
} catch (Throwable throwable) {
|
||||
// We called acquire prior to the try-clause. We need to undo that if we're not creating a composite buffer:
|
||||
close();
|
||||
throw throwable;
|
||||
} finally {
|
||||
if (slices != null) {
|
||||
for (Buffer slice : slices) {
|
||||
if (slice != null) {
|
||||
slice.close(); // Ownership now transfers to the composite buffer.
|
||||
}
|
||||
}
|
||||
if (length > 0) {
|
||||
slices = new Buffer[bufs.length];
|
||||
int off = subOffset;
|
||||
int cap = length;
|
||||
int i;
|
||||
for (i = searchOffsets(offset); cap > 0; i++) {
|
||||
var buf = bufs[i];
|
||||
int avail = buf.capacity() - off;
|
||||
slices[i] = buf.slice(off, Math.min(cap, avail));
|
||||
cap -= avail;
|
||||
off = 0;
|
||||
}
|
||||
slices = Arrays.copyOf(slices, i);
|
||||
} else {
|
||||
// Specialize for length == 0, since we must slice from at least one constituent buffer.
|
||||
slices = new Buffer[] { choice.slice(subOffset, 0) };
|
||||
}
|
||||
|
||||
// Use the constructor that skips filtering out empty buffers, and skips acquiring on the buffers.
|
||||
// This is important because 1) slice() already acquired the buffers, and 2) if this slice is empty
|
||||
// then we need to keep holding on to it to prevent this originating composite buffer from getting
|
||||
// ownership. If it did, its behaviour would be inconsistent with that of a non-composite buffer.
|
||||
return new CompositeBuffer(allocator, slices, COMPOSITE_DROP, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -749,7 +740,7 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
}
|
||||
if (bufs.length == 0) {
|
||||
// Bifurcating a zero-length buffer is trivial.
|
||||
return new CompositeBuffer(allocator, true, bufs, unsafeGetDrop(), true).order(order);
|
||||
return new CompositeBuffer(allocator, bufs, unsafeGetDrop(), true).order(order);
|
||||
}
|
||||
|
||||
int i = searchOffsets(woff);
|
||||
@ -761,7 +752,7 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
}
|
||||
computeBufferOffsets();
|
||||
try {
|
||||
var compositeBuf = new CompositeBuffer(allocator, true, bifs, unsafeGetDrop(), true);
|
||||
var compositeBuf = new CompositeBuffer(allocator, bifs, unsafeGetDrop(), true);
|
||||
compositeBuf.order = order; // Preserve byte order even if bifs array is empty.
|
||||
return compositeBuf;
|
||||
} finally {
|
||||
@ -1164,7 +1155,7 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
for (int i = 0; i < sends.length; i++) {
|
||||
received[i] = sends[i].receive();
|
||||
}
|
||||
var composite = new CompositeBuffer(allocator, true, received, drop, true);
|
||||
var composite = new CompositeBuffer(allocator, received, drop, true);
|
||||
composite.readOnly = readOnly;
|
||||
drop.attach(composite);
|
||||
return composite;
|
||||
@ -1179,18 +1170,9 @@ final class CompositeBuffer extends RcSupport<Buffer, CompositeBuffer> implement
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IllegalStateException notSendableException() {
|
||||
if (!isSendable) {
|
||||
return new IllegalStateException(
|
||||
"Cannot send() this buffer. This buffer might be a slice of another buffer.");
|
||||
}
|
||||
return super.notSendableException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwned() {
|
||||
return isSendable && super.isOwned() && allConstituentsAreOwned();
|
||||
return super.isOwned() && allConstituentsAreOwned();
|
||||
}
|
||||
|
||||
private boolean allConstituentsAreOwned() {
|
||||
|
@ -34,5 +34,6 @@ public interface MemoryManager {
|
||||
Buffer allocateShared(AllocatorControl allo, long size, Drop<Buffer> drop, Cleaner cleaner);
|
||||
Drop<Buffer> drop();
|
||||
Object unwrapRecoverableMemory(Buffer buf);
|
||||
int capacityOfRecoverableMemory(Object memory);
|
||||
Buffer recoverMemory(Object recoverableMemory, Drop<Buffer> drop);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public abstract class RcSupport<I extends Rc<I>, T extends RcSupport<I, T>> impl
|
||||
@Override
|
||||
public final I acquire() {
|
||||
if (acquires < 0) {
|
||||
throw attachTrace(new IllegalStateException("Resource is closed."));
|
||||
throw attachTrace(new IllegalStateException("This resource is closed: " + this + '.'));
|
||||
}
|
||||
if (acquires == Integer.MAX_VALUE) {
|
||||
throw new IllegalStateException("Cannot acquire more references; counter would overflow.");
|
||||
@ -98,7 +98,7 @@ public abstract class RcSupport<I extends Rc<I>, T extends RcSupport<I, T>> impl
|
||||
*/
|
||||
protected IllegalStateException notSendableException() {
|
||||
return new IllegalStateException(
|
||||
"Cannot send() a reference counted object with " + acquires + " outstanding acquires: " + this + '.');
|
||||
"Cannot send() a reference counted object with " + countBorrows() + " borrows: " + this + '.');
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,7 +28,7 @@ class SizeClassedMemoryPool implements BufferAllocator, AllocatorControl, Drop<B
|
||||
private static final VarHandle CLOSE = Statics.findVarHandle(
|
||||
lookup(), SizeClassedMemoryPool.class, "closed", boolean.class);
|
||||
private final MemoryManager manager;
|
||||
private final ConcurrentHashMap<Integer, ConcurrentLinkedQueue<Send<Buffer>>> pool;
|
||||
private final ConcurrentHashMap<Integer, ConcurrentLinkedQueue<Object>> pool;
|
||||
@SuppressWarnings("unused")
|
||||
private volatile boolean closed;
|
||||
|
||||
@ -41,9 +41,9 @@ class SizeClassedMemoryPool implements BufferAllocator, AllocatorControl, Drop<B
|
||||
public Buffer allocate(int size) {
|
||||
BufferAllocator.checkSize(size);
|
||||
var sizeClassPool = getSizeClassPool(size);
|
||||
Send<Buffer> send = sizeClassPool.poll();
|
||||
if (send != null) {
|
||||
return send.receive()
|
||||
Object memory = sizeClassPool.poll();
|
||||
if (memory != null) {
|
||||
return recoverMemoryIntoBuffer(memory)
|
||||
.reset()
|
||||
.readOnly(false)
|
||||
.fill((byte) 0)
|
||||
@ -71,10 +71,10 @@ class SizeClassedMemoryPool implements BufferAllocator, AllocatorControl, Drop<B
|
||||
if (CLOSE.compareAndSet(this, false, true)) {
|
||||
var capturedExceptions = new ArrayList<Exception>(4);
|
||||
pool.forEach((k, v) -> {
|
||||
Send<Buffer> send;
|
||||
while ((send = v.poll()) != null) {
|
||||
Object memory;
|
||||
while ((memory = v.poll()) != null) {
|
||||
try {
|
||||
send.receive().close();
|
||||
dispose(recoverMemoryIntoBuffer(memory));
|
||||
} catch (Exception e) {
|
||||
capturedExceptions.add(e);
|
||||
}
|
||||
@ -94,40 +94,47 @@ class SizeClassedMemoryPool implements BufferAllocator, AllocatorControl, Drop<B
|
||||
dispose(buf);
|
||||
return;
|
||||
}
|
||||
var sizeClassPool = getSizeClassPool(buf.capacity());
|
||||
sizeClassPool.offer(buf.send());
|
||||
Object mem = manager.unwrapRecoverableMemory(buf);
|
||||
var sizeClassPool = getSizeClassPool(manager.capacityOfRecoverableMemory(mem));
|
||||
sizeClassPool.offer(mem);
|
||||
if (closed) {
|
||||
Send<Buffer> send;
|
||||
while ((send = sizeClassPool.poll()) != null) {
|
||||
send.receive().close();
|
||||
Object memory;
|
||||
while ((memory = sizeClassPool.poll()) != null) {
|
||||
dispose(recoverMemoryIntoBuffer(memory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SizeClassedMemoryPool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object allocateUntethered(Buffer originator, int size) {
|
||||
var sizeClassPool = getSizeClassPool(size);
|
||||
Send<Buffer> send = sizeClassPool.poll();
|
||||
Buffer untetheredBuf;
|
||||
if (send != null) {
|
||||
var transfer = (TransferSend<Buffer, Buffer>) send;
|
||||
var owned = transfer.unsafeUnwrapOwned();
|
||||
untetheredBuf = owned.transferOwnership(NO_OP_DROP);
|
||||
} else {
|
||||
untetheredBuf = createBuf(size, NO_OP_DROP);
|
||||
Object memory = sizeClassPool.poll();
|
||||
if (memory == null) {
|
||||
Buffer untetheredBuf = createBuf(size, NO_OP_DROP);
|
||||
memory = manager.unwrapRecoverableMemory(untetheredBuf);
|
||||
}
|
||||
return manager.unwrapRecoverableMemory(untetheredBuf);
|
||||
return memory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recoverMemory(Object memory) {
|
||||
var drop = getDrop();
|
||||
var buf = manager.recoverMemory(memory, drop);
|
||||
drop.attach(buf);
|
||||
Buffer buf = recoverMemoryIntoBuffer(memory);
|
||||
buf.close();
|
||||
}
|
||||
|
||||
private ConcurrentLinkedQueue<Send<Buffer>> getSizeClassPool(int size) {
|
||||
private Buffer recoverMemoryIntoBuffer(Object memory) {
|
||||
var drop = getDrop();
|
||||
var buf = manager.recoverMemory(memory, drop);
|
||||
drop.attach(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private ConcurrentLinkedQueue<Object> getSizeClassPool(int size) {
|
||||
return pool.computeIfAbsent(size, k -> new ConcurrentLinkedQueue<>());
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,15 @@ import java.lang.ref.Cleaner;
|
||||
|
||||
interface Statics {
|
||||
Cleaner CLEANER = Cleaner.create();
|
||||
Drop<Buffer> NO_OP_DROP = buf -> {
|
||||
Drop<Buffer> NO_OP_DROP = new Drop<Buffer>() {
|
||||
@Override
|
||||
public void drop(Buffer obj) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NO_OP_DROP";
|
||||
}
|
||||
};
|
||||
|
||||
static VarHandle findVarHandle(Lookup lookup, Class<?> recv, String name, Class<?> type) {
|
||||
|
@ -19,6 +19,8 @@ import io.netty.buffer.ByteBufConvertible;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.DuplicatedByteBuf;
|
||||
import io.netty.buffer.SlicedByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.buffer.api.Buffer;
|
||||
import io.netty.buffer.api.BufferAllocator;
|
||||
@ -957,8 +959,8 @@ public final class ByteBufAdaptor extends ByteBuf {
|
||||
|
||||
@Override
|
||||
public ByteBuf readSlice(int length) {
|
||||
ByteBuf slice = readRetainedSlice(length);
|
||||
release();
|
||||
ByteBuf slice = slice(readerIndex(), length);
|
||||
buffer.readerOffset(buffer.readerOffset() + length);
|
||||
return slice;
|
||||
}
|
||||
|
||||
@ -1411,22 +1413,18 @@ public final class ByteBufAdaptor extends ByteBuf {
|
||||
|
||||
@Override
|
||||
public ByteBuf slice() {
|
||||
ByteBuf slice = retainedSlice();
|
||||
release();
|
||||
return slice;
|
||||
return slice(readerIndex(), readableBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf retainedSlice() {
|
||||
checkAccess();
|
||||
return wrap(buffer.slice());
|
||||
return retainedSlice(readerIndex(), readableBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf slice(int index, int length) {
|
||||
ByteBuf slice = retainedSlice(index, length);
|
||||
release();
|
||||
return slice;
|
||||
checkAccess();
|
||||
return new Slice(this, index, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1439,11 +1437,55 @@ public final class ByteBufAdaptor extends ByteBuf {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Slice extends SlicedByteBuf {
|
||||
private final int indexAdjustment;
|
||||
private final int lengthAdjustment;
|
||||
|
||||
Slice(ByteBuf buffer, int index, int length) {
|
||||
super(buffer, index, length);
|
||||
indexAdjustment = index;
|
||||
lengthAdjustment = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf retainedDuplicate() {
|
||||
return new Slice(unwrap().retainedDuplicate(), indexAdjustment, lengthAdjustment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf retainedSlice(int index, int length) {
|
||||
checkIndex(index, length);
|
||||
return unwrap().retainedSlice(indexAdjustment + index, length);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Duplicate extends DuplicatedByteBuf {
|
||||
Duplicate(ByteBufAdaptor byteBuf) {
|
||||
super(byteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf duplicate() {
|
||||
((ByteBufAdaptor) unwrap()).checkAccess();
|
||||
return new Duplicate((ByteBufAdaptor) unwrap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf retainedDuplicate() {
|
||||
return unwrap().retainedDuplicate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf retainedSlice(int index, int length) {
|
||||
return unwrap().retainedSlice(index, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf duplicate() {
|
||||
ByteBuf duplicate = retainedDuplicate();
|
||||
release();
|
||||
return duplicate;
|
||||
checkAccess();
|
||||
Duplicate duplicatedByteBuf = new Duplicate(this);
|
||||
return duplicatedByteBuf.setIndex(readerIndex(), writerIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,7 +34,7 @@ public abstract class AbstractMemorySegmentManager implements MemoryManager {
|
||||
if (cleaner != null) {
|
||||
segment = segment.registerCleaner(cleaner);
|
||||
}
|
||||
return new MemSegBuffer(segment, convert(drop), alloc);
|
||||
return new MemSegBuffer(segment, segment, convert(drop), alloc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,7 +43,7 @@ public abstract class AbstractMemorySegmentManager implements MemoryManager {
|
||||
if (cleaner != null) {
|
||||
segment = segment.registerCleaner(cleaner);
|
||||
}
|
||||
return new MemSegBuffer(segment, convert(drop), alloc);
|
||||
return new MemSegBuffer(segment, segment, convert(drop), alloc);
|
||||
}
|
||||
|
||||
protected abstract MemorySegment createSegment(long size);
|
||||
@ -59,6 +59,11 @@ public abstract class AbstractMemorySegmentManager implements MemoryManager {
|
||||
return b.recoverableMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacityOfRecoverableMemory(Object memory) {
|
||||
return ((RecoverableMemory) memory).capacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Buffer recoverMemory(Object recoverableMemory, Drop<Buffer> drop) {
|
||||
var recovery = (RecoverableMemory) recoverableMemory;
|
||||
|
@ -20,33 +20,47 @@ import io.netty.buffer.api.Drop;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
class BifurcatedDrop implements Drop<MemSegBuffer> {
|
||||
final class ArcDrop implements Drop<MemSegBuffer> {
|
||||
private static final VarHandle COUNT;
|
||||
static {
|
||||
try {
|
||||
COUNT = MethodHandles.lookup().findVarHandle(BifurcatedDrop.class, "count", int.class);
|
||||
COUNT = MethodHandles.lookup().findVarHandle(ArcDrop.class, "count", int.class);
|
||||
} catch (Exception e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final MemSegBuffer originalBuf;
|
||||
private final Drop<MemSegBuffer> delegate;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private volatile int count;
|
||||
|
||||
BifurcatedDrop(MemSegBuffer originalBuf, Drop<MemSegBuffer> delegate) {
|
||||
this.originalBuf = originalBuf;
|
||||
ArcDrop(Drop<MemSegBuffer> delegate) {
|
||||
this.delegate = delegate;
|
||||
count = 2; // These are created by buffer bifurcation, so we initially have 2 references to this drop.
|
||||
count = 1;
|
||||
}
|
||||
|
||||
void increment() {
|
||||
static Drop<MemSegBuffer> wrap(Drop<MemSegBuffer> drop) {
|
||||
if (drop.getClass() == ArcDrop.class) {
|
||||
return drop;
|
||||
}
|
||||
return new ArcDrop(drop);
|
||||
}
|
||||
|
||||
static Drop<MemSegBuffer> acquire(Drop<MemSegBuffer> drop) {
|
||||
if (drop.getClass() == ArcDrop.class) {
|
||||
((ArcDrop) drop).increment();
|
||||
return drop;
|
||||
}
|
||||
return new ArcDrop(drop);
|
||||
}
|
||||
|
||||
ArcDrop increment() {
|
||||
int c;
|
||||
do {
|
||||
c = count;
|
||||
checkValidState(c);
|
||||
} while (!COUNT.compareAndSet(this, c, c + 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,10 +73,8 @@ class BifurcatedDrop implements Drop<MemSegBuffer> {
|
||||
checkValidState(c);
|
||||
} while (!COUNT.compareAndSet(this, c, n));
|
||||
if (n == 0) {
|
||||
delegate.attach(originalBuf);
|
||||
delegate.drop(originalBuf);
|
||||
delegate.drop(buf);
|
||||
}
|
||||
buf.makeInaccessible();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,10 +82,28 @@ class BifurcatedDrop implements Drop<MemSegBuffer> {
|
||||
delegate.attach(obj);
|
||||
}
|
||||
|
||||
boolean isOwned() {
|
||||
return count <= 1;
|
||||
}
|
||||
|
||||
int countBorrows() {
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
Drop<MemSegBuffer> unwrap() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder().append("ArcDrop(").append(count).append(", ");
|
||||
Drop<MemSegBuffer> drop = this;
|
||||
while ((drop = ((ArcDrop) drop).unwrap()) instanceof ArcDrop) {
|
||||
builder.append(((ArcDrop) drop).count).append(", ");
|
||||
}
|
||||
return builder.append(drop).append(')').toString();
|
||||
}
|
||||
|
||||
private static void checkValidState(int count) {
|
||||
if (count == 0) {
|
||||
throw new IllegalStateException("Underlying resources have already been freed.");
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package io.netty.buffer.api.memseg;
|
||||
|
||||
import io.netty.buffer.api.Buffer;
|
||||
import io.netty.buffer.api.Drop;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
public class HeapMemorySegmentManager extends AbstractMemorySegmentManager {
|
||||
@ -29,15 +27,4 @@ public class HeapMemorySegmentManager extends AbstractMemorySegmentManager {
|
||||
protected MemorySegment createSegment(long size) {
|
||||
return MemorySegment.ofArray(new byte[Math.toIntExact(size)]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drop<Buffer> drop() {
|
||||
return convert(buf -> buf.makeInaccessible());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
|
||||
private static Drop<Buffer> convert(Drop<MemSegBuffer> drop) {
|
||||
Drop<?> tmp = drop;
|
||||
return (Drop<Buffer>) tmp;
|
||||
}
|
||||
}
|
||||
|
@ -58,32 +58,72 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
static {
|
||||
CLOSED_SEGMENT = MemorySegment.ofArray(new byte[0]);
|
||||
CLOSED_SEGMENT.close();
|
||||
SEGMENT_CLOSE = buf -> {
|
||||
try (var ignore = buf.seg) {
|
||||
buf.makeInaccessible();
|
||||
SEGMENT_CLOSE = new Drop<MemSegBuffer>() {
|
||||
@Override
|
||||
public void drop(MemSegBuffer buf) {
|
||||
buf.base.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SEGMENT_CLOSE";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final AllocatorControl alloc;
|
||||
private final boolean isSendable;
|
||||
private MemorySegment base;
|
||||
private MemorySegment seg;
|
||||
private MemorySegment wseg;
|
||||
private ByteOrder order;
|
||||
private int roff;
|
||||
private int woff;
|
||||
|
||||
MemSegBuffer(MemorySegment segmet, Drop<MemSegBuffer> drop, AllocatorControl alloc) {
|
||||
this(segmet, drop, alloc, true);
|
||||
MemSegBuffer(MemorySegment base, MemorySegment view, Drop<MemSegBuffer> drop, AllocatorControl alloc) {
|
||||
super(new MakeInaccisbleOnDrop(ArcDrop.wrap(drop)));
|
||||
this.alloc = alloc;
|
||||
this.base = base;
|
||||
seg = view;
|
||||
wseg = view;
|
||||
order = ByteOrder.nativeOrder();
|
||||
}
|
||||
|
||||
private MemSegBuffer(MemorySegment segment, Drop<MemSegBuffer> drop, AllocatorControl alloc, boolean isSendable) {
|
||||
super(drop);
|
||||
this.alloc = alloc;
|
||||
seg = segment;
|
||||
wseg = segment;
|
||||
this.isSendable = isSendable;
|
||||
order = ByteOrder.nativeOrder();
|
||||
private static final class MakeInaccisbleOnDrop implements Drop<MemSegBuffer> {
|
||||
final Drop<MemSegBuffer> delegate;
|
||||
|
||||
private MakeInaccisbleOnDrop(Drop<MemSegBuffer> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(MemSegBuffer buf) {
|
||||
try {
|
||||
delegate.drop(buf);
|
||||
} finally {
|
||||
buf.makeInaccessible();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach(MemSegBuffer buf) {
|
||||
delegate.attach(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemSegDrop(" + delegate + ')';
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drop<MemSegBuffer> unsafeGetDrop() {
|
||||
MakeInaccisbleOnDrop drop = (MakeInaccisbleOnDrop) super.unsafeGetDrop();
|
||||
return drop.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsafeSetDrop(Drop<MemSegBuffer> replacement) {
|
||||
super.unsafeSetDrop(new MakeInaccisbleOnDrop(replacement));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -233,14 +273,12 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length cannot be negative: " + length + '.');
|
||||
}
|
||||
if (!isAccessible()) {
|
||||
throw new IllegalStateException("This buffer is closed: " + this + '.');
|
||||
}
|
||||
var slice = seg.asSlice(offset, length);
|
||||
acquire();
|
||||
Drop<MemSegBuffer> drop = b -> {
|
||||
close();
|
||||
b.makeInaccessible();
|
||||
};
|
||||
var sendable = false; // Sending implies ownership change, which we can't do for slices.
|
||||
return new MemSegBuffer(slice, drop, alloc, sendable)
|
||||
Drop<MemSegBuffer> drop = ArcDrop.acquire(unsafeGetDrop());
|
||||
return new MemSegBuffer(base, slice, drop, alloc)
|
||||
.writerOffset(length)
|
||||
.order(order())
|
||||
.readOnly(readOnly());
|
||||
@ -482,19 +520,22 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
|
||||
// Release old memory segment:
|
||||
var drop = unsafeGetDrop();
|
||||
if (drop instanceof BifurcatedDrop) {
|
||||
// Disconnect from the bifurcated drop, since we'll get our own fresh memory segment.
|
||||
if (drop instanceof ArcDrop) {
|
||||
// Disconnect from the current arc drop, since we'll get our own fresh memory segment.
|
||||
int roff = this.roff;
|
||||
int woff = this.woff;
|
||||
drop.drop(this);
|
||||
drop = ((BifurcatedDrop) drop).unwrap();
|
||||
unsafeSetDrop(drop);
|
||||
while (drop instanceof ArcDrop) {
|
||||
drop = ((ArcDrop) drop).unwrap();
|
||||
}
|
||||
unsafeSetDrop(new ArcDrop(drop));
|
||||
this.roff = roff;
|
||||
this.woff = woff;
|
||||
} else {
|
||||
alloc.recoverMemory(recoverableMemory());
|
||||
}
|
||||
|
||||
base = newSegment;
|
||||
seg = newSegment;
|
||||
wseg = newSegment;
|
||||
drop.attach(this);
|
||||
@ -505,19 +546,10 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
if (!isOwned()) {
|
||||
throw attachTrace(new IllegalStateException("Cannot bifurcate a buffer that is not owned."));
|
||||
}
|
||||
var drop = unsafeGetDrop();
|
||||
if (seg.ownerThread() != null) {
|
||||
seg = seg.share();
|
||||
drop.attach(this);
|
||||
}
|
||||
if (drop instanceof BifurcatedDrop) {
|
||||
((BifurcatedDrop) drop).increment();
|
||||
} else {
|
||||
drop = new BifurcatedDrop(new MemSegBuffer(seg, drop, alloc), drop);
|
||||
unsafeSetDrop(drop);
|
||||
}
|
||||
var drop = (ArcDrop) unsafeGetDrop();
|
||||
unsafeSetDrop(new ArcDrop(drop));
|
||||
var bifurcatedSeg = seg.asSlice(0, woff);
|
||||
var bifurcatedBuf = new MemSegBuffer(bifurcatedSeg, drop, alloc);
|
||||
var bifurcatedBuf = new MemSegBuffer(base, bifurcatedSeg, new ArcDrop(drop.increment()), alloc);
|
||||
bifurcatedBuf.woff = woff;
|
||||
bifurcatedBuf.roff = roff;
|
||||
bifurcatedBuf.order(order);
|
||||
@ -1052,11 +1084,12 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
var readOnly = readOnly();
|
||||
boolean isConfined = seg.ownerThread() == null;
|
||||
MemorySegment transferSegment = isConfined? seg : seg.share();
|
||||
MemorySegment base = this.base;
|
||||
makeInaccessible();
|
||||
return new Owned<MemSegBuffer>() {
|
||||
@Override
|
||||
public MemSegBuffer transferOwnership(Drop<MemSegBuffer> drop) {
|
||||
MemSegBuffer copy = new MemSegBuffer(transferSegment, drop, alloc);
|
||||
MemSegBuffer copy = new MemSegBuffer(base, transferSegment, drop, alloc);
|
||||
copy.order = order;
|
||||
copy.roff = roff;
|
||||
copy.woff = woff;
|
||||
@ -1067,6 +1100,7 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
}
|
||||
|
||||
void makeInaccessible() {
|
||||
base = CLOSED_SEGMENT;
|
||||
seg = CLOSED_SEGMENT;
|
||||
wseg = CLOSED_SEGMENT;
|
||||
roff = 0;
|
||||
@ -1074,17 +1108,13 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IllegalStateException notSendableException() {
|
||||
if (!isSendable) {
|
||||
return new IllegalStateException(
|
||||
"Cannot send() this buffer. This buffer might be a slice of another buffer.");
|
||||
}
|
||||
return super.notSendableException();
|
||||
public boolean isOwned() {
|
||||
return super.isOwned() && ((ArcDrop) unsafeGetDrop()).isOwned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwned() {
|
||||
return isSendable && super.isOwned();
|
||||
public int countBorrows() {
|
||||
return super.countBorrows() + ((ArcDrop) unsafeGetDrop()).countBorrows();
|
||||
}
|
||||
|
||||
private void checkRead(int index, int size) {
|
||||
@ -1153,7 +1183,7 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
}
|
||||
|
||||
Object recoverableMemory() {
|
||||
return new RecoverableMemory(seg, alloc);
|
||||
return new RecoverableMemory(base, alloc);
|
||||
}
|
||||
|
||||
// <editor-fold name="BufferIntegratable methods">
|
||||
@ -1226,7 +1256,11 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
||||
}
|
||||
|
||||
Buffer recover(Drop<MemSegBuffer> drop) {
|
||||
return new MemSegBuffer(segment, drop, alloc);
|
||||
return new MemSegBuffer(segment, segment, ArcDrop.acquire(drop), alloc);
|
||||
}
|
||||
|
||||
int capacity() {
|
||||
return (int) segment.byteSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,6 @@ public class BufferTest {
|
||||
|
||||
private static final Memoize<Fixture[]> ALL_COMBINATIONS = new Memoize<>(
|
||||
() -> fixtureCombinations().toArray(Fixture[]::new));
|
||||
private static final Memoize<Fixture[]> NON_SLICED = new Memoize<>(
|
||||
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> !f.isSlice()).toArray(Fixture[]::new));
|
||||
private static final Memoize<Fixture[]> NON_COMPOSITE = new Memoize<>(
|
||||
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> !f.isComposite()).toArray(Fixture[]::new));
|
||||
private static final Memoize<Fixture[]> HEAP_ALLOCS = new Memoize<>(
|
||||
@ -78,10 +76,6 @@ public class BufferTest {
|
||||
return ALL_COMBINATIONS.get();
|
||||
}
|
||||
|
||||
static Fixture[] nonSliceAllocators() {
|
||||
return NON_SLICED.get();
|
||||
}
|
||||
|
||||
static Fixture[] nonCompositeAllocators() {
|
||||
return NON_COMPOSITE.get();
|
||||
}
|
||||
@ -349,7 +343,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
void allocateAndSendToThread(Fixture fixture) throws Exception {
|
||||
try (BufferAllocator allocator = fixture.createAllocator()) {
|
||||
ArrayBlockingQueue<Send<Buffer>> queue = new ArrayBlockingQueue<>(10);
|
||||
@ -369,7 +363,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
void allocateAndSendToThreadViaSyncQueue(Fixture fixture) throws Exception {
|
||||
SynchronousQueue<Send<Buffer>> queue = new SynchronousQueue<>();
|
||||
Future<Byte> future = executor.submit(() -> {
|
||||
@ -388,7 +382,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
void sendMustThrowWhenBufIsAcquired(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -403,7 +397,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void originalBufferMustNotBeAccessibleAfterSend(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer orig = allocator.allocate(24)) {
|
||||
@ -505,7 +499,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void cannotSendMoreThanOnce(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -822,7 +816,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
void sendOnSliceWithoutOffsetAndSizeMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -837,7 +831,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
void sendOnSliceWithOffsetAndSizeMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -926,6 +920,51 @@ public class BufferTest {
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonCompositeAllocators")
|
||||
public void acquireComposingAndSlicingMustIncrementBorrowsWithData(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
buf.writeByte((byte) 1);
|
||||
int borrows = buf.countBorrows();
|
||||
try (Buffer ignored = buf.acquire()) {
|
||||
assertEquals(borrows + 1, buf.countBorrows());
|
||||
try (Buffer slice = buf.slice()) {
|
||||
assertEquals(1, slice.capacity());
|
||||
int sliceBorrows = slice.countBorrows();
|
||||
assertEquals(borrows + 2, buf.countBorrows());
|
||||
try (Buffer ignored1 = Buffer.compose(allocator, buf, slice)) {
|
||||
assertEquals(borrows + 3, buf.countBorrows());
|
||||
assertEquals(sliceBorrows + 1, slice.countBorrows());
|
||||
}
|
||||
assertEquals(sliceBorrows, slice.countBorrows());
|
||||
assertEquals(borrows + 2, buf.countBorrows());
|
||||
}
|
||||
assertEquals(borrows + 1, buf.countBorrows());
|
||||
}
|
||||
assertEquals(borrows, buf.countBorrows());
|
||||
}
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@ParameterizedTest
|
||||
@MethodSource("allocators")
|
||||
public void sliceMustBecomeOwnedOnSourceBufferClose(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator()) {
|
||||
Buffer buf = allocator.allocate(8);
|
||||
buf.writeInt(42);
|
||||
try (Buffer slice = buf.slice()) {
|
||||
buf.close();
|
||||
assertFalse(buf.isAccessible());
|
||||
assertTrue(slice.isOwned());
|
||||
try (Buffer receive = slice.send().receive()) {
|
||||
assertTrue(receive.isOwned());
|
||||
assertFalse(slice.isAccessible());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allocators")
|
||||
void copyIntoByteArray(Fixture fixture) {
|
||||
@ -1737,7 +1776,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableMustThrowForNegativeSize(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -1746,7 +1785,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableMustThrowIfRequestedSizeWouldGrowBeyondMaxAllowed(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(512)) {
|
||||
@ -1755,7 +1794,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableMustNotThrowWhenSpaceIsAlreadyAvailable(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -1766,7 +1805,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableMustExpandBufferCapacity(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -1801,7 +1840,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void mustBeAbleToSliceAfterEnsureWritable(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(4)) {
|
||||
@ -1816,7 +1855,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableOnCompositeBuffersMustRespectExistingBigEndianByteOrder(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator()) {
|
||||
Buffer composite;
|
||||
@ -1833,7 +1872,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableOnCompositeBuffersMustRespectExistingLittleEndianByteOrder(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator()) {
|
||||
Buffer composite;
|
||||
@ -1850,7 +1889,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableWithCompactionMustNotAllocateIfCompactionIsEnough(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(64)) {
|
||||
@ -2175,7 +2214,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcateOfNonOwnedBufferMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2188,7 +2227,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcatedPartMustContainFirstHalfOfBuffer(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16).order(BIG_ENDIAN)) {
|
||||
@ -2224,7 +2263,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcatedPartsMustBeIndividuallySendable(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16).order(BIG_ENDIAN)) {
|
||||
@ -2253,7 +2292,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void mustBePossibleToBifurcateMoreThanOnce(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16).order(BIG_ENDIAN)) {
|
||||
@ -2281,7 +2320,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcatedBufferMustHaveSameByteOrderAsParent(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8).order(BIG_ENDIAN)) {
|
||||
@ -2299,7 +2338,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableOnBifurcatedBuffers(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2318,7 +2357,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableOnBifurcatedBuffersWithOddOffsets(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(10).order(BIG_ENDIAN)) {
|
||||
@ -2367,7 +2406,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcatedBuffersMustBeAccessibleInOtherThreads(Fixture fixture) throws Exception {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2387,7 +2426,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void sendMustNotMakeBifurcatedBuffersInaccessible(Fixture fixture) throws Exception {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16)) {
|
||||
@ -2411,7 +2450,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void compactMustDiscardReadBytes(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16, BIG_ENDIAN)) {
|
||||
@ -2433,7 +2472,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void compactMustThrowForUnownedBuffer(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8, BIG_ENDIAN)) {
|
||||
@ -2548,7 +2587,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void readOnlyBufferMustRemainReadOnlyAfterSend(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2613,7 +2652,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void bifurcateOfReadOnlyBufferMustBeReadOnly(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(16)) {
|
||||
@ -2665,7 +2704,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void compactOnReadOnlyBufferMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2675,7 +2714,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void ensureWritableOnReadOnlyBufferMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer buf = allocator.allocate(8)) {
|
||||
@ -2685,7 +2724,7 @@ public class BufferTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nonSliceAllocators")
|
||||
@MethodSource("allocators")
|
||||
public void copyIntoOnReadOnlyBufferMustThrow(Fixture fixture) {
|
||||
try (BufferAllocator allocator = fixture.createAllocator();
|
||||
Buffer dest = allocator.allocate(8)) {
|
||||
|
@ -39,36 +39,6 @@ public class ByteBufAdaptorTest extends AbstractByteBufTest {
|
||||
return alloc.buffer(capacity, capacity);
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testSliceReadGatheringByteChannelMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testDuplicateReadGatheringByteChannelMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testSliceReadOutputStreamMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testDuplicateReadOutputStreamMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testSliceBytesInArrayMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("New buffers not thread-safe like this.")
|
||||
@Override
|
||||
public void testDuplicateBytesInArrayMultipleThreads() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore("This test codifies that asking to reading 0 bytes from an empty but unclosed stream should return -1, " +
|
||||
"which is just weird.")
|
||||
@Override
|
||||
@ -112,152 +82,8 @@ public class ByteBufAdaptorTest extends AbstractByteBufTest {
|
||||
public void testToByteBuffer2() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateUnreleasable3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateUnreleasable4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateAndRetainedSliceContentIsExpected() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedSliceReleaseOriginal2() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedSliceReleaseOriginal3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedSliceReleaseOriginal4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testReadRetainedSliceUnreleasable3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testReadRetainedSliceUnreleasable4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedSliceUnreleasable3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedSliceUnreleasable4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedSliceReleaseOriginal2() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedSliceReleaseOriginal3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedSliceReleaseOriginal4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedDuplicateReleaseOriginal2() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedDuplicateReleaseOriginal3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testMultipleRetainedDuplicateReleaseOriginal4() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateReleaseOriginal2() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateReleaseOriginal3() {
|
||||
}
|
||||
|
||||
@Ignore("This assumes a single reference count for the memory, but all buffers (views of memory) have " +
|
||||
"independent reference counts now. Also, this plays tricks with reference that we cannot support.")
|
||||
@Override
|
||||
public void testRetainedDuplicateReleaseOriginal4() {
|
||||
}
|
||||
|
||||
@Ignore("No longer allowed to allocate 0 sized buffers, except for composite buffers with no components.")
|
||||
@Override
|
||||
public void testLittleEndianWithExpand() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testRetainedSliceAfterReleaseRetainedSliceDuplicate() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testRetainedSliceAfterReleaseRetainedDuplicateSlice() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testSliceAfterReleaseRetainedSliceDuplicate() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testDuplicateAfterReleaseRetainedSliceDuplicate() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testDuplicateAfterReleaseRetainedDuplicateSlice() {
|
||||
}
|
||||
|
||||
@Ignore("Test seems to inherently have double-free bug?")
|
||||
@Override
|
||||
public void testSliceAfterReleaseRetainedDuplicateSlice() {
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user