Fix (some) failing tests

Also introduce test sampling, so when the BufferTest is running from an IDE, only 3% of the tests will actually run.

The Maven build runs all tests.
This commit is contained in:
Chris Vest 2021-04-07 16:19:35 +02:00
parent 513cef1c1e
commit ab45a7b053
4 changed files with 48 additions and 14 deletions

View File

@ -173,6 +173,9 @@
<argLine>${argLine.common} ${argLine.printGC} --add-modules jdk.incubator.foreign</argLine> <argLine>${argLine.common} ${argLine.printGC} --add-modules jdk.incubator.foreign</argLine>
<!-- Ensure the whole stacktrace is preserved when an exception is thrown. See https://issues.apache.org/jira/browse/SUREFIRE-1457 --> <!-- Ensure the whole stacktrace is preserved when an exception is thrown. See https://issues.apache.org/jira/browse/SUREFIRE-1457 -->
<trimStackTrace>false</trimStackTrace> <trimStackTrace>false</trimStackTrace>
<systemProperties>
<sample>nosample</sample>
</systemProperties>
</configuration> </configuration>
<dependencies> <dependencies>
<!-- Declare the surefire dynamic dependencies explicitly, to speed up the docker build. --> <!-- Declare the surefire dynamic dependencies explicitly, to speed up the docker build. -->

View File

@ -60,12 +60,19 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
static { static {
try (ResourceScope scope = ResourceScope.newSharedScope()) { try (ResourceScope scope = ResourceScope.newSharedScope()) {
CLOSED_SEGMENT = MemorySegment.allocateNative(1, scope); // We are not allowed to allocate a zero-sized native buffer, but we *can* take a zero-sized slice from it.
// We need the CLOSED_SEGMENT to have a size of zero, because we'll use its size for bounds checks after
// the buffer is closed.
MemorySegment segment = MemorySegment.allocateNative(1, scope);
CLOSED_SEGMENT = segment.asSlice(0, 0);
} }
SEGMENT_CLOSE = new Drop<MemSegBuffer>() { SEGMENT_CLOSE = new Drop<MemSegBuffer>() {
@Override @Override
public void drop(MemSegBuffer buf) { public void drop(MemSegBuffer buf) {
buf.base.scope().close(); ResourceScope scope = buf.base.scope();
if (!scope.isImplicit()) {
scope.close();
}
} }
@Override @Override

View File

@ -22,6 +22,8 @@ import java.lang.ref.Cleaner;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import static jdk.incubator.foreign.ResourceScope.newSharedScope;
public class NativeMemorySegmentManager extends AbstractMemorySegmentManager { public class NativeMemorySegmentManager extends AbstractMemorySegmentManager {
public static final LongAdder MEM_USAGE_NATIVE = new LongAdder(); public static final LongAdder MEM_USAGE_NATIVE = new LongAdder();
private static final ConcurrentHashMap<Long, Runnable> CLEANUP_ACTIONS = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Long, Runnable> CLEANUP_ACTIONS = new ConcurrentHashMap<>();
@ -37,7 +39,7 @@ public class NativeMemorySegmentManager extends AbstractMemorySegmentManager {
@Override @Override
protected MemorySegment createSegment(long size, Cleaner cleaner) { protected MemorySegment createSegment(long size, Cleaner cleaner) {
final ResourceScope scope = ResourceScope.newSharedScope(cleaner); final ResourceScope scope = cleaner == null ? newSharedScope() : newSharedScope(cleaner);
scope.addOnClose(getCleanupAction(size)); scope.addOnClose(getCleanupAction(size));
var segment = MemorySegment.allocateNative(size, scope); var segment = MemorySegment.allocateNative(size, scope);
MEM_USAGE_NATIVE.add(size); MEM_USAGE_NATIVE.add(size);

View File

@ -31,10 +31,14 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException; import java.nio.ReadOnlyBufferException;
import java.text.ParseException; import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.SplittableRandom;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -43,6 +47,7 @@ import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.Stream.Builder; import java.util.stream.Stream.Builder;
@ -64,7 +69,7 @@ public class BufferTest {
private static ExecutorService executor; private static ExecutorService executor;
private static final Memoize<Fixture[]> ALL_COMBINATIONS = new Memoize<>( private static final Memoize<Fixture[]> ALL_COMBINATIONS = new Memoize<>(
() -> fixtureCombinations().toArray(Fixture[]::new)); () -> fixtureCombinations().filter(sample()).toArray(Fixture[]::new));
private static final Memoize<Fixture[]> NON_COMPOSITE = new Memoize<>( private static final Memoize<Fixture[]> NON_COMPOSITE = new Memoize<>(
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> !f.isComposite()).toArray(Fixture[]::new)); () -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> !f.isComposite()).toArray(Fixture[]::new));
private static final Memoize<Fixture[]> HEAP_ALLOCS = new Memoize<>( private static final Memoize<Fixture[]> HEAP_ALLOCS = new Memoize<>(
@ -73,6 +78,19 @@ public class BufferTest {
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> f.isDirect()).toArray(Fixture[]::new)); () -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> f.isDirect()).toArray(Fixture[]::new));
private static final Memoize<Fixture[]> POOLED_ALLOCS = new Memoize<>( private static final Memoize<Fixture[]> POOLED_ALLOCS = new Memoize<>(
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> f.isPooled()).toArray(Fixture[]::new)); () -> Arrays.stream(ALL_COMBINATIONS.get()).filter(f -> f.isPooled()).toArray(Fixture[]::new));
private static final Memoize<Fixture[]> POOLED_DIRECT_ALLOCS = new Memoize<>(
() -> Arrays.stream(ALL_COMBINATIONS.get()).filter(
f -> f.isPooled() && f.isDirect()).toArray(Fixture[]::new));
private static Predicate<Fixture> sample() {
String sampleSetting = System.getProperty("sample");
if ("nosample".equalsIgnoreCase(sampleSetting)) {
return fixture -> true;
}
Instant today = Instant.now().truncatedTo(ChronoUnit.DAYS);
SplittableRandom rng = new SplittableRandom(today.hashCode());
return fixture -> rng.nextInt(0, 100) <= 2; // Filter out 97% of tests.
}
static Fixture[] allocators() { static Fixture[] allocators() {
return ALL_COMBINATIONS.get(); return ALL_COMBINATIONS.get();
@ -94,6 +112,10 @@ public class BufferTest {
return POOLED_ALLOCS.get(); return POOLED_ALLOCS.get();
} }
static Fixture[] pooledDirectAllocators() {
return POOLED_DIRECT_ALLOCS.get();
}
static List<Fixture> initialAllocators() { static List<Fixture> initialAllocators() {
return List.of( return List.of(
new Fixture("heap", BufferAllocator::heap, HEAP), new Fixture("heap", BufferAllocator::heap, HEAP),
@ -962,7 +984,7 @@ public class BufferTest {
} }
} }
@Disabled @Disabled // TODO
@ParameterizedTest @ParameterizedTest
@MethodSource("allocators") @MethodSource("allocators")
public void sliceMustBecomeOwnedOnSourceBufferClose(Fixture fixture) { public void sliceMustBecomeOwnedOnSourceBufferClose(Fixture fixture) {
@ -1650,42 +1672,42 @@ public class BufferTest {
@Nested @Nested
@Isolated @Isolated
class CleanerTests { class CleanerTests {
@Disabled("Precise native memory accounting does not work since recent panama-foreign changes.")
@ParameterizedTest @ParameterizedTest
@MethodSource("io.netty.buffer.api.BufTest#directAllocators") @MethodSource("io.netty.buffer.api.BufferTest#directAllocators")
public void bufferMustBeClosedByCleaner(Fixture fixture) throws InterruptedException { public void bufferMustBeClosedByCleaner(Fixture fixture) throws InterruptedException {
var initial = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum();
var allocator = fixture.createAllocator(); var allocator = fixture.createAllocator();
allocator.close(); allocator.close();
int iterations = 100; int iterations = 50;
int allocationSize = 1024; int allocationSize = 1024;
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize); allocateAndForget(allocator, allocationSize);
System.gc(); System.gc();
System.runFinalization(); System.runFinalization();
} }
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum(); var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
var totalAllocated = (long) allocationSize * iterations; var totalAllocated = (long) allocationSize * iterations;
assertThat(sum).isLessThan(totalAllocated); assertThat(sum).isLessThan(totalAllocated);
} }
private void allocateAndForget(BufferAllocator allocator, int size) { private static void allocateAndForget(BufferAllocator allocator, int size) {
allocator.allocate(size); allocator.allocate(size);
} }
@Disabled("Precise native memory accounting does not work since recent panama-foreign changes.")
@ParameterizedTest @ParameterizedTest
@MethodSource("io.netty.buffer.api.BufTest#directPooledAllocators") @MethodSource("io.netty.buffer.api.BufferTest#pooledDirectAllocators")
public void buffersMustBeReusedByPoolingAllocatorEvenWhenDroppedByCleanerInsteadOfExplicitly(Fixture fixture) public void buffersMustBeReusedByPoolingAllocatorEvenWhenDroppedByCleanerInsteadOfExplicitly(Fixture fixture)
throws InterruptedException { throws InterruptedException {
var initial = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum();
try (var allocator = fixture.createAllocator()) { try (var allocator = fixture.createAllocator()) {
int iterations = 100; int iterations = 50;
int allocationSize = 1024; int allocationSize = 1024;
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
allocateAndForget(allocator, allocationSize); allocateAndForget(allocator, allocationSize);
System.gc(); System.gc();
System.runFinalization(); System.runFinalization();
} }
var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum(); var sum = NativeMemorySegmentManager.MEM_USAGE_NATIVE.sum() - initial;
var totalAllocated = (long) allocationSize * iterations; var totalAllocated = (long) allocationSize * iterations;
assertThat(sum).isLessThan(totalAllocated); assertThat(sum).isLessThan(totalAllocated);
} }