mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
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:
parent
670b4567cf
commit
4461eeb31d
10 changed files with 823 additions and 183 deletions
|
@ -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) { }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue