Merge pull request #36 from netty/rc-thread-safety

Allow slices to obtain ownership when parent is closed
This commit is contained in:
Chris Vest 2021-03-16 21:27:46 +01:00 committed by GitHub
commit 0272b1cf84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 377 additions and 396 deletions

View File

@ -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;

View File

@ -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() {

View File

@ -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);
}

View File

@ -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

View File

@ -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<>());
}

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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.");

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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)) {

View File

@ -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() {
}
}