< prev index next > test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java
Print this page
*/
/*
* @test id=default
* @summary Test virtual threads using Object.wait/notifyAll
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
* @modules java.base/java.lang:+open jdk.management
* @library /test/lib
+ * @build LockingMode
* @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify
*/
+ /*
+ * @test id=LM_LEGACY
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=LM_LIGHTWEIGHT
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xint-LM_LEGACY
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xint -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xint-LM_LIGHTWEIGHT
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xint -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-LM_LEGACY
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-LM_LIGHTWEIGHT
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-TieredStopAtLevel1-LM_LEGACY
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-TieredStopAtLevel1-LM_LIGHTWEIGHT
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-noTieredCompilation-LM_LEGACY
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
+ /*
+ * @test id=Xcomp-noTieredCompilation-LM_LIGHTWEIGHT
+ * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
+ * @modules java.base/java.lang:+open jdk.management
+ * @library /test/lib
+ * @build LockingMode
+ * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
+ */
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import jdk.test.lib.thread.VThreadScheduler;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadPinner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
+ import org.junit.jupiter.api.condition.DisabledIf;
+ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.ParameterizedTest;
+ import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
class MonitorWaitNotify {
lock.notifyAll();
}
thread.join();
}
}
+
+ /**
+ * Returns a stream of elements that are ordered pairs of platform and virtual thread
+ * counts. 0,2,4,..8 platform threads. 2,4,6,..16 virtual threads.
+ */
+ static Stream<Arguments> threadCounts() {
+ return IntStream.range(0, 9)
+ .filter(i -> i % 2 == 0)
+ .mapToObj(i -> i)
+ .flatMap(np -> IntStream.range(2, 17)
+ .filter(i -> i % 2 == 0)
+ .mapToObj(vp -> Arguments.of(np, vp)));
+ }
+
+ /**
+ * Test notify wakes only one thread when platform and virtual threads are waiting.
+ */
+ @ParameterizedTest
+ @MethodSource("threadCounts")
+ @DisabledIf("LockingMode#isLegacy")
+ void testNotifyOneThread(int nPlatformThreads, int nVirtualThreads) throws Exception {
+ int nThreads = nPlatformThreads + nVirtualThreads;
+
+ var lock = new Object();
+ var ready = new CountDownLatch(nThreads);
+ var notified = new AtomicInteger();
+
+ Runnable waitTask = () -> {
+ synchronized (lock) {
+ try {
+ ready.countDown();
+ lock.wait();
+ notified.incrementAndGet();
+ } catch (InterruptedException e) { }
+ }
+ };
+
+ var threads = new ArrayList<Thread>();
+ try {
+ for (int i = 0; i < nPlatformThreads; i++) {
+ threads.add(Thread.ofPlatform().start(waitTask));
+ }
+ for (int i = 0; i < nVirtualThreads; i++) {
+ threads.add(Thread.ofVirtual().start(waitTask));
+ }
+
+ // wait for all threads to wait
+ ready.await();
+
+ // wake threads, one by one
+ for (int i = 0; i < threads.size(); i++) {
+
+ // wake one thread
+ synchronized (lock) {
+ lock.notify();
+ }
+
+ // one thread should have awoken
+ int expectedWakeups = i + 1;
+ while (notified.get() < expectedWakeups) {
+ Thread.sleep(10);
+ }
+ assertEquals(expectedWakeups, notified.get());
+ }
+ } finally {
+ for (Thread t : threads) {
+ t.interrupt();
+ t.join();
+ }
+ }
+ }
+
+ /**
+ * Test notifyAll wakes all threads.
+ */
+ @ParameterizedTest
+ @MethodSource("threadCounts")
+ @DisabledIf("LockingMode#isLegacy")
+ void testNotifyAllThreads(int nPlatformThreads, int nVirtualThreads) throws Exception {
+ int nThreads = nPlatformThreads + nVirtualThreads;
+
+ var lock = new Object();
+ var ready = new CountDownLatch(nThreads);
+ var notified = new CountDownLatch(nThreads);
+
+ Runnable waitTask = () -> {
+ synchronized (lock) {
+ try {
+ ready.countDown();
+ lock.wait();
+ notified.countDown();
+ } catch (InterruptedException e) { }
+ }
+ };
+
+ var threads = new ArrayList<Thread>();
+ try {
+ for (int i = 0; i < nPlatformThreads; i++) {
+ threads.add(Thread.ofPlatform().start(waitTask));
+ }
+ for (int i = 0; i < nVirtualThreads; i++) {
+ threads.add(Thread.ofVirtual().start(waitTask));
+ }
+
+ // wait for all threads to wait
+ ready.await();
+
+ // wakeup all threads
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+
+ // wait for all threads to have awoken
+ notified.await();
+
+ } finally {
+ for (Thread t : threads) {
+ t.interrupt();
+ t.join();
+ }
+ }
+ }
+
/**
* Test duration of timed Object.wait.
*/
@Test
void testTimedWaitDuration1() throws Exception {
// thread should terminate
vthread.join();
assertTrue(completed.get());
}
+ /**
+ * Test that Object.wait releases the carrier. This test uses a custom scheduler
+ * with one carrier thread.
+ */
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE })
+ @DisabledIf("LockingMode#isLegacy")
+ void testReleaseWhenWaiting1(int timeout) throws Exception {
+ assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers");
+ try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
+ ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler);
+
+ var lock = new Object();
+ var ready = new AtomicBoolean();
+ var completed = new AtomicBoolean();
+
+ var vthread1 = factory.newThread(() -> {
+ synchronized (lock) {
+ try {
+ ready.set(true);
+ if (timeout > 0) {
+ lock.wait(timeout);
+ } else {
+ lock.wait();
+ }
+ } catch (InterruptedException e) {
+ fail("wait interrupted");
+ }
+ }
+ completed.set(true);
+ });
+ vthread1.start();
+
+ // wait for vthread1 to start and wait
+ awaitTrue(ready);
+ await(vthread1, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING);
+
+ // carrier should be released, use it for another thread
+ var executed = new AtomicBoolean();
+ var vthread2 = factory.newThread(() -> {
+ executed.set(true);
+ });
+ vthread2.start();
+ vthread2.join();
+ assertTrue(executed.get());
+
+ // wakeup vthread1
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+
+ vthread1.join();
+ assertTrue(completed.get());
+ }
+ }
+
+ /**
+ * Test that Object.wait releases the carrier. This test arranges for 4*ncores - 1
+ * virtual threads to wait. For long timeout and no timeout cases, all virtual threads
+ * will wait until they are notified.
+ */
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 10, 20, 100, 500, 30000, Integer.MAX_VALUE })
+ @DisabledIf("LockingMode#isLegacy")
+ void testReleaseWhenWaiting2(int timeout) throws Exception {
+ int VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors();
+ CountDownLatch latch = new CountDownLatch(VTHREAD_COUNT);
+ Object lock = new Object();
+ AtomicInteger counter = new AtomicInteger(0);
+
+ for (int i = 0; i < VTHREAD_COUNT; i++) {
+ Thread.ofVirtual().name("vthread-" + i).start(() -> {
+ synchronized (lock) {
+ if (counter.incrementAndGet() == VTHREAD_COUNT) {
+ lock.notifyAll();
+ } else {
+ try {
+ if (timeout > 0) {
+ lock.wait(timeout);
+ } else {
+ lock.wait();
+ }
+ } catch (InterruptedException e) {}
+ }
+ }
+ latch.countDown();
+ });
+ }
+ latch.await();
+ }
+
/**
* Test that wait(long) throws IAE when timeout is negative.
*/
@Test
void testIllegalArgumentException() throws Exception {
< prev index next >