Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public void setup() {
private static void runConcurrently(
boolean usePooledMemory, int nThreads, Function<Integer, Runnable> runnableSupplier)
throws ExecutionException, InterruptedException, TimeoutException {
try (ExecutorService parallelExecutor = Executors.newFixedThreadPool(nThreads)) {
ExecutorService parallelExecutor = Executors.newFixedThreadPool(nThreads);
try {
if (usePooledMemory) {
CuVSProvider.provider().enableRMMPooledMemory(10, 60);
}
Expand All @@ -64,22 +65,22 @@ private static void runConcurrently(
futures[j] = CompletableFuture.runAsync(runnableSupplier.apply(j), parallelExecutor);
}

CompletableFuture.allOf(futures)
.exceptionally(
t -> {
log.error("Exception while executing runnable", t);
fail("Exception while executing runnable: " + unwrap(t));
return null;
})
.get(2000, TimeUnit.SECONDS);

parallelExecutor.shutdown();
assertTrue(
"Timeout waiting for parallelExecutor to finish",
parallelExecutor.awaitTermination(10, TimeUnit.SECONDS));
try {
CompletableFuture.allOf(futures).get(2000, TimeUnit.SECONDS);
} catch (ExecutionException e) {
log.error("Exception while executing runnable", e);
fail("Exception while executing runnable: " + unwrap(e));
}
} finally {
if (usePooledMemory) {
CuVSProvider.provider().resetRMMPooledMemory();
try {
parallelExecutor.shutdown();
assertTrue(
"Timeout waiting for parallelExecutor to finish",
parallelExecutor.awaitTermination(60, TimeUnit.SECONDS));
} finally {
if (usePooledMemory) {
CuVSProvider.provider().resetRMMPooledMemory();
}
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion java/cuvs-java/src/test/java/com/nvidia/cuvs/CuVSTestCase.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.nvidia.cuvs;
Expand All @@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;

import com.carrotsearch.randomizedtesting.RandomizedContext;
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.BitSet;
Expand All @@ -19,6 +20,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Base class for the cuVS integration tests.
*
* <p>The lingering is not cosmetic. A {@link java.util.concurrent.ThreadPoolExecutor} reaches
* TERMINATED as soon as its worker count drops to zero, which happens in {@code getTask()} before
* the workers have finished {@code processWorkerExit()}, so {@code awaitTermination()} returns
* while the worker threads are still alive. {@code ThreadLeakLingering} defaults to no wait at
* all, which leaves the leak check sampling straight into that window and reporting threads that
* are in the middle of dying. Waiting a few seconds for them costs nothing when there is no leak,
* and a real one still fails the test.
*/
@ThreadLeakLingering(linger = 5000)
public abstract class CuVSTestCase {
protected Random random;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Expand Down
Loading