8312498: Thread::getState and JVM TI GetThreadState should return TIMED_WAITING virtual thread is timed parked

Reviewed-by: sspitsyn, rpressler
This commit is contained in:
Alan Bateman 2023-09-19 10:58:12 +00:00
parent 670b4567cf
commit 4461eeb31d
10 changed files with 823 additions and 183 deletions

View file

@ -86,41 +86,52 @@ final class VirtualThread extends BaseVirtualThread {
private volatile int state;
/*
* Virtual thread state and transitions:
* Virtual thread state transitions:
*
* NEW -> STARTED // Thread.start
* NEW -> STARTED // Thread.start, schedule to run
* STARTED -> TERMINATED // failed to start
* STARTED -> RUNNING // first run
* RUNNING -> TERMINATED // done
*
* RUNNING -> PARKING // Thread attempts to park
* PARKING -> PARKED // cont.yield successful, thread is parked
* PARKING -> PINNED // cont.yield failed, thread is pinned
* RUNNING -> PARKING // Thread parking with LockSupport.park
* PARKING -> PARKED // cont.yield successful, parked indefinitely
* PARKING -> PINNED // cont.yield failed, parked indefinitely on carrier
* PARKED -> RUNNABLE // unparked, schedule to continue
* PINNED -> RUNNING // unparked, continue execution on same carrier
*
* PARKED -> RUNNABLE // unpark or interrupted
* PINNED -> RUNNABLE // unpark or interrupted
* RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos
* TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked
* TIMED_PARKING -> TIMED_PINNED // cont.yield failed, timed-parked on carrier
* TIMED_PARKED -> RUNNABLE // unparked, schedule to continue
* TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier
*
* RUNNABLE -> RUNNING // continue execution
*
* RUNNING -> YIELDING // Thread.yield
* YIELDING -> RUNNABLE // yield successful
* YIELDING -> RUNNING // yield failed
*
* RUNNING -> TERMINATED // done
*/
private static final int NEW = 0;
private static final int STARTED = 1;
private static final int RUNNABLE = 2; // runnable-unmounted
private static final int RUNNING = 3; // runnable-mounted
// untimed parking
private static final int PARKING = 4;
private static final int PARKED = 5; // unmounted
private static final int PINNED = 6; // mounted
private static final int YIELDING = 7; // Thread.yield
// timed parking
private static final int TIMED_PARKING = 7;
private static final int TIMED_PARKED = 8;
private static final int TIMED_PINNED = 9;
private static final int YIELDING = 10; // Thread.yield
private static final int TERMINATED = 99; // final state
// can be suspended from scheduling when unmounted
private static final int SUSPENDED = 1 << 8;
private static final int RUNNABLE_SUSPENDED = (RUNNABLE | SUSPENDED);
private static final int PARKED_SUSPENDED = (PARKED | SUSPENDED);
// parking permit
private volatile boolean parkPermit;
@ -434,17 +445,18 @@ final class VirtualThread extends BaseVirtualThread {
* If yielding due to Thread.yield then it just submits the task to continue.
*/
private void afterYield() {
int s = state();
assert (s == PARKING || s == YIELDING) && (carrierThread == null);
assert carrierThread == null;
if (s == PARKING) {
setState(PARKED);
int s = state();
if (s == PARKING || s == TIMED_PARKING) {
int newState = (s == PARKING) ? PARKED : TIMED_PARKED;
setState(newState);
// notify JVMTI that unmount has completed, thread is parked
notifyJvmtiUnmount(/*hide*/false);
// may have been unparked while parking
if (parkPermit && compareAndSetState(PARKED, RUNNABLE)) {
if (parkPermit && compareAndSetState(newState, RUNNABLE)) {
// lazy submit to continue on the current thread as carrier if possible
if (currentThread() instanceof CarrierThread ct) {
lazySubmitRunContinuation(ct.getPool());
@ -465,6 +477,8 @@ final class VirtualThread extends BaseVirtualThread {
} else {
submitRunContinuation();
}
} else {
assert false;
}
}
@ -607,13 +621,13 @@ final class VirtualThread extends BaseVirtualThread {
boolean yielded = false;
Future<?> unparker = scheduleUnpark(this::unpark, nanos);
setState(PARKING);
setState(TIMED_PARKING);
try {
yielded = yieldContinuation(); // may throw
} finally {
assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING));
if (!yielded) {
assert state() == PARKING;
assert state() == TIMED_PARKING;
setState(RUNNING);
}
cancel(unparker);
@ -645,7 +659,7 @@ final class VirtualThread extends BaseVirtualThread {
event = null;
}
setState(PINNED);
setState(timed ? TIMED_PINNED : PINNED);
try {
if (!parkPermit) {
if (!timed) {
@ -713,7 +727,8 @@ final class VirtualThread extends BaseVirtualThread {
Thread currentThread = Thread.currentThread();
if (!getAndSetParkPermit(true) && currentThread != this) {
int s = state();
if (s == PARKED && compareAndSetState(PARKED, RUNNABLE)) {
boolean parked = (s == PARKED) || (s == TIMED_PARKED);
if (parked && compareAndSetState(s, RUNNABLE)) {
if (currentThread instanceof VirtualThread vthread) {
vthread.switchToCarrierThread();
try {
@ -724,11 +739,11 @@ final class VirtualThread extends BaseVirtualThread {
} else {
submitRunContinuation();
}
} else if (s == PINNED) {
} else if ((s == PINNED) || (s == TIMED_PINNED)) {
// unpark carrier thread when pinned.
synchronized (carrierThreadAccessLock()) {
Thread carrier = carrierThread;
if (carrier != null && state() == PINNED) {
if (carrier != null && ((s = state()) == PINNED || s == TIMED_PINNED)) {
U.unpark(carrier);
}
}
@ -865,7 +880,8 @@ final class VirtualThread extends BaseVirtualThread {
@Override
Thread.State threadState() {
switch (state()) {
int s = state();
switch (s & ~SUSPENDED) {
case NEW:
return Thread.State.NEW;
case STARTED:
@ -876,7 +892,6 @@ final class VirtualThread extends BaseVirtualThread {
return Thread.State.RUNNABLE;
}
case RUNNABLE:
case RUNNABLE_SUSPENDED:
// runnable, not mounted
return Thread.State.RUNNABLE;
case RUNNING:
@ -890,13 +905,16 @@ final class VirtualThread extends BaseVirtualThread {
// runnable, mounted
return Thread.State.RUNNABLE;
case PARKING:
case TIMED_PARKING:
case YIELDING:
// runnable, mounted, not yet waiting
return Thread.State.RUNNABLE;
case PARKED:
case PARKED_SUSPENDED:
case PINNED:
return Thread.State.WAITING;
return State.WAITING;
case TIMED_PARKED:
case TIMED_PINNED:
return State.TIMED_WAITING;
case TERMINATED:
return Thread.State.TERMINATED;
default:
@ -936,7 +954,7 @@ final class VirtualThread extends BaseVirtualThread {
private StackTraceElement[] tryGetStackTrace() {
int initialState = state();
return switch (initialState) {
case RUNNABLE, PARKED -> {
case RUNNABLE, PARKED, TIMED_PARKED -> {
int suspendedState = initialState | SUSPENDED;
if (compareAndSetState(initialState, suspendedState)) {
try {
@ -948,7 +966,7 @@ final class VirtualThread extends BaseVirtualThread {
// re-submit if runnable
// re-submit if unparked while suspended
if (initialState == RUNNABLE
|| (parkPermit && compareAndSetState(PARKED, RUNNABLE))) {
|| (parkPermit && compareAndSetState(initialState, RUNNABLE))) {
try {
submitRunContinuation();
} catch (RejectedExecutionException ignore) { }