Skip to content

Commit

Permalink
8320707: Virtual thread test updates
Browse files Browse the repository at this point in the history
Reviewed-by: simonis
Backport-of: b67b71cd87c62f15d5b73f923c300d0f77c988f5
  • Loading branch information
shipilev committed Apr 25, 2024
1 parent 955fcc1 commit 41237fc
Show file tree
Hide file tree
Showing 14 changed files with 538 additions and 220 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@
* @test id=default
* @bug 8312498
* @summary Basic test for JVMTI GetThreadState with virtual threads
* @library /test/lib
* @run junit/othervm/native GetThreadStateTest
*/

/*
* @test id=no-vmcontinuations
* @requires vm.continuations
* @library /test/lib
* @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations GetThreadStateTest
*/

import java.util.StringJoiner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

import jdk.test.lib.thread.VThreadPinner;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -75,10 +77,10 @@ void testTerminated() throws Exception {
*/
@Test
void testRunnable() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
var done = new AtomicBoolean();
var thread = Thread.ofVirtual().start(() -> {
latch.countDown();
started.set(true);

// spin until done
while (!done.get()) {
Expand All @@ -87,7 +89,7 @@ void testRunnable() throws Exception {
});
try {
// wait for thread to start execution
latch.await();
awaitTrue(started);

// thread should be runnable
int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE;
Expand All @@ -107,17 +109,17 @@ void testRunnable() throws Exception {
*/
@Test
void testMonitorEnter() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
Object lock = new Object();
var thread = Thread.ofVirtual().unstarted(() -> {
latch.countDown();
started.set(true);
synchronized (lock) { }
});
try {
synchronized (lock) {
// start thread and wait for it to start execution
thread.start();
latch.await();
awaitTrue(started);

// thread should block on monitor enter
int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
Expand All @@ -137,19 +139,19 @@ void testMonitorEnter() throws Exception {
*/
@Test
void testObjectWait() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
Object lock = new Object();
var thread = Thread.ofVirtual().start(() -> {
synchronized (lock) {
latch.countDown();
started.set(true);
try {
lock.wait();
} catch (InterruptedException e) { }
}
});
try {
// wait for thread to own monitor
latch.await();
// wait for thread to start execution
awaitTrue(started);

// thread should wait
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand Down Expand Up @@ -179,19 +181,19 @@ void testObjectWait() throws Exception {
*/
@Test
void testObjectWaitMillis() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
Object lock = new Object();
var thread = Thread.ofVirtual().start(() -> {
synchronized (lock) {
latch.countDown();
started.set(true);
try {
lock.wait(Long.MAX_VALUE);
} catch (InterruptedException e) { }
}
});
try {
// wait for thread to own monitor
latch.await();
// wait for thread to start execution
awaitTrue(started);

// thread should wait
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand Down Expand Up @@ -221,17 +223,17 @@ void testObjectWaitMillis() throws Exception {
*/
@Test
void testPark() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
var done = new AtomicBoolean();
var thread = Thread.ofVirtual().start(() -> {
latch.countDown();
started.set(true);
while (!done.get()) {
LockSupport.park();
}
});
try {
// wait for thread to start execution
latch.await();
awaitTrue(started);

// thread should park
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand All @@ -251,17 +253,17 @@ void testPark() throws Exception {
*/
@Test
void testParkNanos() throws Exception {
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
var done = new AtomicBoolean();
var thread = Thread.ofVirtual().start(() -> {
latch.countDown();
started.set(true);
while (!done.get()) {
LockSupport.parkNanos(Long.MAX_VALUE);
}
});
try {
// wait for thread to start execution
latch.await();
awaitTrue(started);

// thread should park
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand All @@ -281,20 +283,19 @@ void testParkNanos() throws Exception {
*/
@Test
void testParkWhenPinned() throws Exception {
var latch = new CountDownLatch(1);
Object lock = new Object();
var started = new AtomicBoolean();
var done = new AtomicBoolean();
var thread = Thread.ofVirtual().start(() -> {
synchronized (lock) {
latch.countDown();
VThreadPinner.runPinned(() -> {
started.set(true);
while (!done.get()) {
LockSupport.park();
}
}
});
});
try {
// wait for thread to own monitor
latch.await();
// wait for thread to start execution
awaitTrue(started);

// thread should park
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand All @@ -314,20 +315,19 @@ void testParkWhenPinned() throws Exception {
*/
@Test
void testParkNanosWhenPinned() throws Exception {
var latch = new CountDownLatch(1);
Object lock = new Object();
var started = new AtomicBoolean();
var done = new AtomicBoolean();
var thread = Thread.ofVirtual().start(() -> {
synchronized (lock) {
latch.countDown();
VThreadPinner.runPinned(() -> {
started.set(true);
while (!done.get()) {
LockSupport.parkNanos(Long.MAX_VALUE);
}
}
});
});
try {
// wait for thread to own monitor
latch.await();
// wait for thread to start execution
awaitTrue(started);

// thread should park
int expected = JVMTI_THREAD_STATE_ALIVE |
Expand All @@ -342,6 +342,15 @@ void testParkNanosWhenPinned() throws Exception {
}
}

/**
* Waits for the boolean value to become true.
*/
private static void awaitTrue(AtomicBoolean ref) throws Exception {
while (!ref.get()) {
Thread.sleep(20);
}
}

/**
* Asserts that the given thread has the expected JVMTI state.
*/
Expand Down
2 changes: 1 addition & 1 deletion test/jdk/java/lang/ScopedValue/StressStackOverflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ static class TestFailureException extends RuntimeException {
TestFailureException(String s) { super(s); }
}

static final long DURATION_IN_NANOS = Duration.ofMinutes(2).toNanos();
static final long DURATION_IN_NANOS = Duration.ofMinutes(1).toNanos();

// Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
// and Runnable interfaces. Which one gets tested depends on the constructor argument.
Expand Down
42 changes: 28 additions & 14 deletions test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -55,38 +54,53 @@ class CarrierThreadWaits {
void testCarrierThreadWaiting() throws Exception {
try (ForkJoinPool pool = new ForkJoinPool(1)) {
var carrierRef = new AtomicReference<Thread>();
var vthreadRef = new AtomicReference<Thread>();

Executor scheduler = task -> {
pool.submit(() -> {
carrierRef.set(Thread.currentThread());
Thread carrier = Thread.currentThread();
carrierRef.set(carrier);
Thread vthread = vthreadRef.get();

System.err.format("%s run task (%s) ...%n", carrier, vthread);
task.run();
System.err.format("%s task done (%s)%n", carrier, vthread);
});
};

// start a virtual thread that spins and remains mounted until "done"
var latch = new CountDownLatch(1);
var started = new AtomicBoolean();
var done = new AtomicBoolean();
Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
Thread vthread = builder.start(() -> {
latch.countDown();
Thread vthread = builder.unstarted(() -> {
started.set(true);
while (!done.get()) {
Thread.onSpinWait();
}
});

// wait for virtual thread to execute
latch.await();
vthreadRef.set(vthread);
vthread.start();

try {
long carrierId = carrierRef.get().threadId();
// wait for virtual thread to start
while (!started.get()) {
Thread.sleep(10);
}

Thread carrier = carrierRef.get();

long carrierId = carrier.threadId();
long vthreadId = vthread.threadId();

// carrier thread should be on WAITING on virtual thread
ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId);
assertTrue(ti.getThreadState() == Thread.State.WAITING);
assertEquals(vthread.getClass().getName(), ti.getLockInfo().getClassName());
assertTrue(ti.getLockInfo().getIdentityHashCode() == System.identityHashCode(vthread));
assertTrue(ti.getLockOwnerId() == vthreadId);

Thread.State state = ti.getThreadState();
LockInfo lockInfo = ti.getLockInfo();
assertEquals(Thread.State.WAITING, state);
assertNotNull(lockInfo);
assertEquals(vthread.getClass().getName(), lockInfo.getClassName());
assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode());
assertEquals(vthreadId, ti.getLockOwnerId());
} finally {
done.set(true);
}
Expand Down
45 changes: 24 additions & 21 deletions test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,34 @@
*/

import java.io.IOException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.Selector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

public class GetStackTraceWhenRunnable {

public static void main(String[] args) throws Exception {
try (Selector sel = Selector.open()) {

// start thread1 and wait for it to park
Thread thread1 = Thread.startVirtualThread(LockSupport::park);
while (thread1.getState() != Thread.State.WAITING) {
Thread.sleep(20);
}
// start thread1 and wait for it to park
Thread thread1 = Thread.startVirtualThread(LockSupport::park);
while (thread1.getState() != Thread.State.WAITING) {
Thread.sleep(20);
}

// start thread2 to pin the carrier thread
CountDownLatch latch = new CountDownLatch(1);
Thread thread2 = Thread.startVirtualThread(() -> {
latch.countDown();
try {
sel.select();
} catch (ClosedSelectorException e) {
// expected
} catch (IOException ioe) {
ioe.printStackTrace();
}
});
latch.await(); // wait for thread2 to run
// start thread2 to pin the carrier thread
var started = new AtomicBoolean();
var done = new AtomicBoolean();
Thread thread2 = Thread.startVirtualThread(() -> {
started.set(true);
while (!done.get()) {
Thread.onSpinWait();
}
});
try {
// wait for thread2 to start
while (!started.get()) {
Thread.sleep(10);
}

// unpark thread1 and check that it is "stuck" in the runnable state
// (the carrier thread is pinned, no other virtual thread can run)
Expand All @@ -73,6 +72,10 @@ public static void main(String[] args) throws Exception {
for (StackTraceElement e : stack) {
System.out.println(e);
}
} finally {
done.set(true);
thread2.join();
thread1.join();
}
}

Expand Down
Loading

1 comment on commit 41237fc

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.