Skip to content

Commit 5a018cd

Browse files
authored
Merge pull request #21514 from fengxue-IS/051-jep491-monitorEnter
(v0.51.0) Redesign preparePinnedVirtualThreadForUnmount logic
2 parents f4b79e1 + 9a09b31 commit 5a018cd

File tree

4 files changed

+124
-35
lines changed

4 files changed

+124
-35
lines changed

runtime/codert_vm/cnathelp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,11 @@ slow_jitMonitorEnterImpl(J9VMThread *currentThread, bool forMethod)
17161716
if ((J9_OBJECT_MONITOR_BLOCKING == monstatus) && VM_ContinuationHelpers::isYieldableVirtualThread(currentThread)) {
17171717
/* Try to yield the virtual thread if it will be blocked. */
17181718
monstatus = currentThread->javaVM->internalVMFunctions->preparePinnedVirtualThreadForUnmount(currentThread, (j9object_t)currentThread->floatTemp2, false);
1719+
if (monstatus > J9_OBJECT_MONITOR_BLOCKING) {
1720+
/* Monitor has been acquired. */
1721+
addr = restoreJITResolveFrame(currentThread, oldPC, forMethod, false);
1722+
goto done;
1723+
}
17191724
}
17201725
#endif /* JAVA_SPEC_VERSION >= 24 */
17211726
if (monstatus < J9_OBJECT_MONITOR_BLOCKING) {

runtime/oti/vm_api.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4734,7 +4734,8 @@ preparePinnedVirtualThreadForMount(J9VMThread *currentThread, j9object_t continu
47344734
* @param syncObj object to block/wait on
47354735
* @param isObjectWait if the call is from Object.wait()
47364736
*
4737-
* @return J9_OBJECT_MONITOR_YIELD_VIRTUAL if the can be successfully yielded;
4737+
* @return syncObj if isObjectWait is false and monitor can be acquired;
4738+
* J9_OBJECT_MONITOR_YIELD_VIRTUAL if the virtual thread can be successfully yielded;
47384739
* otherwise, an error code is returned
47394740
*/
47404741
UDATA

runtime/vm/BytecodeInterpreter.hpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,8 @@ class INTERPRETER_CLASS
15271527
VMINLINE VM_BytecodeAction
15281528
yieldPinnedContinuation(REGISTER_ARGS_LIST, U_32 newThreadState, UDATA returnState)
15291529
{
1530+
J9VMContinuation *continuation = _currentThread->currentContinuation;
1531+
15301532
/* InternalNative frame only build for non-jit calls. */
15311533
if (J9VM_CONTINUATION_RETURN_FROM_JIT_MONITOR_ENTER != returnState) {
15321534
buildInternalNativeStackFrame(REGISTER_ARGS);
@@ -1537,8 +1539,13 @@ class INTERPRETER_CLASS
15371539
if (JAVA_LANG_VIRTUALTHREAD_BLOCKING == newThreadState) {
15381540
/* Add the thread object to the blocked list. */
15391541
omrthread_monitor_enter(_vm->blockedVirtualThreadsMutex);
1540-
_currentThread->currentContinuation->nextWaitingContinuation = _vm->blockedContinuations;
1541-
_vm->blockedContinuations = _currentThread->currentContinuation;
1542+
continuation->nextWaitingContinuation = _vm->blockedContinuations;
1543+
_vm->blockedContinuations = continuation;
1544+
1545+
if (NULL == continuation->objectWaitMonitor->monitor->owner) {
1546+
/* notify unblocker if the blocking monitor is unlocked. */
1547+
omrthread_monitor_notify(_vm->blockedVirtualThreadsMutex);
1548+
}
15421549
omrthread_monitor_exit(_vm->blockedVirtualThreadsMutex);
15431550
}
15441551

@@ -1578,9 +1585,6 @@ class INTERPRETER_CLASS
15781585
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
15791586
case J9_OBJECT_MONITOR_YIELD_VIRTUAL: {
15801587
rc = yieldPinnedContinuation(REGISTER_ARGS, JAVA_LANG_VIRTUALTHREAD_BLOCKING, returnState);
1581-
omrthread_monitor_enter(_vm->blockedVirtualThreadsMutex);
1582-
omrthread_monitor_notify(_vm->blockedVirtualThreadsMutex);
1583-
omrthread_monitor_exit(_vm->blockedVirtualThreadsMutex);
15841588
break;
15851589
}
15861590
case J9_OBJECT_MONITOR_OOM:
@@ -5763,6 +5767,7 @@ class INTERPRETER_CLASS
57635767
j9object_t waitObject = *(j9object_t *)(_sp + 3);
57645768
rc = tryEnterBlockingMonitor(REGISTER_ARGS, waitObject, J9VM_CONTINUATION_RETURN_FROM_OBJECT_WAIT);
57655769
if ((NULL != _currentThread->currentContinuation) && (EXECUTE_BYTECODE == rc)) {
5770+
waitObject = *(j9object_t *)(_sp + 3);
57665771
omrthread_monitor_t monitor = getMonitorForWait(_currentThread, waitObject);
57675772
monitor->count = _currentThread->currentContinuation->waitingMonitorEnterCount;
57685773
_currentThread->currentContinuation->waitingMonitorEnterCount = 0;

runtime/vm/ContinuationHelpers.cpp

Lines changed: 107 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -923,14 +923,108 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
923923
UDATA result = J9_OBJECT_MONITOR_YIELD_VIRTUAL;
924924
J9ObjectMonitor *syncObjectMonitor = NULL;
925925
J9ObjectMonitor *enteredMonitorsList = NULL;
926-
j9objectmonitor_t lock = 0;
927-
j9object_t continuationObj = NULL;
928926
UDATA monitorCount = 0;
929927
J9JavaVM *vm = currentThread->javaVM;
930928

931929
if (NULL != syncObj) {
930+
j9objectmonitor_t volatile *lwEA = VM_ObjectMonitor::inlineGetLockAddress(currentThread, syncObj);
931+
j9objectmonitor_t lock = J9_LOAD_LOCKWORD(currentThread, lwEA);
932+
omrthread_monitor_t monitor = NULL;
933+
932934
enterVThreadTransitionCritical(currentThread, (jobject)&currentThread->threadObject);
935+
936+
if (J9_LOCK_IS_INFLATED(lock)) {
937+
syncObjectMonitor = J9_INFLLOCK_OBJECT_MONITOR(lock);
938+
} else {
939+
if (isObjectWait) {
940+
syncObjectMonitor = objectMonitorInflate(currentThread, syncObj, lock);
941+
if (NULL == syncObjectMonitor) {
942+
result = J9_OBJECT_MONITOR_OOM;
943+
goto done;
944+
}
945+
} else {
946+
/* This must be a monitor enter case, so this implies that a monitor entry was
947+
* created as the non-blocking path would have failed.
948+
* Since a monitor can only be inflated by a thread that owns it, we can't directly
949+
* inflate the blocking monitor.
950+
*/
951+
restart:
952+
#if defined(J9VM_THR_LOCK_RESERVATION)
953+
{
954+
while (OBJECT_HEADER_LOCK_RESERVED == (lock & (OBJECT_HEADER_LOCK_RESERVED | OBJECT_HEADER_LOCK_INFLATED))) {
955+
cancelLockReservation(currentThread);
956+
/* Calculate the new lock word, since the object may have moved. */
957+
syncObj = J9VMTHREAD_BLOCKINGENTEROBJECT(currentThread, currentThread);
958+
lwEA = VM_ObjectMonitor::inlineGetLockAddress(currentThread, syncObj);
959+
lock = J9_LOAD_LOCKWORD(currentThread, lwEA);
960+
if (VM_ObjectMonitor::inlineFastInitAndEnterMonitor(currentThread, lwEA)) {
961+
result = (UDATA)syncObj;
962+
goto done;
963+
}
964+
}
965+
}
966+
#endif /* J9VM_THR_LOCK_RESERVATION */
967+
syncObjectMonitor = monitorTableAt(currentThread, syncObj);
968+
monitor = syncObjectMonitor->monitor;
969+
970+
/* Try acquire the inflated monitor */
971+
if (0 == omrthread_monitor_try_enter(monitor)) {
972+
/* If the INFLATED bit is set, then it is already inflated and we own the object monitor. */
973+
if (J9_ARE_ANY_BITS_SET(((J9ThreadMonitor *)monitor)->flags, J9THREAD_MONITOR_INFLATED)) {
974+
currentThread->ownedMonitorCount += 1;
975+
result = (UDATA)syncObj;
976+
goto done;
977+
}
978+
979+
/* Loop until either lock is inflated or FLC bit is set. */
980+
while (!J9_LOCK_IS_INFLATED(lock)) {
981+
/* The monitor isn't inflated yet, but we can update the lockword now. */
982+
UDATA newLockword = 0;
983+
UDATA count = J9_FLATLOCK_COUNT(lock);
984+
if (0 == count) {
985+
/* Lock is unlocked, so try to directly acquire it as a flatlock. */
986+
newLockword = (UDATA)currentThread;
987+
} else {
988+
/* Change the lock to flat with FLC bit set so it will be inflated during exit. */
989+
newLockword = (UDATA)J9_FLATLOCK_OWNER(lock)
990+
| ((count - 1) << OBJECT_HEADER_LOCK_V2_RECURSION_OFFSET)
991+
| OBJECT_HEADER_LOCK_FLC;
992+
}
993+
j9objectmonitor_t const oldValue = lock;
994+
lock = VM_ObjectMonitor::compareAndSwapLockword(currentThread, lwEA, lock, (j9objectmonitor_t)newLockword);
995+
if (lock == oldValue) {
996+
/* CAS succeeded, we can proceed with using the inflated monitor. */
997+
VM_ObjectMonitor::incrementCancelCounter(J9OBJECT_CLAZZ(currentThread, syncObj));
998+
999+
if (J9_FLATLOCK_OWNER(lock) == currentThread) {
1000+
/* Lock is acquired. */
1001+
currentThread->ownedMonitorCount += 1;
1002+
result = (UDATA)syncObj;
1003+
goto done;
1004+
}
1005+
break;
1006+
#if defined(J9VM_THR_LOCK_RESERVATION)
1007+
} else if (OBJECT_HEADER_LOCK_RESERVED == (lock & (OBJECT_HEADER_LOCK_RESERVED | OBJECT_HEADER_LOCK_INFLATED))) {
1008+
/* Lock is now reserved, exit the inflated monitor and restart to cancel lock reservation. */
1009+
omrthread_monitor_exit(monitor);
1010+
goto restart;
1011+
#endif /* J9VM_THR_LOCK_RESERVATION */
1012+
}
1013+
/* CAS failed, another thread must have updated the lockword, retry the check. */
1014+
}
1015+
} else {
1016+
/* Inflated monitor owned by another thread, so the lockword update will be completed by them. */
1017+
lock = J9_LOAD_LOCKWORD(currentThread, lwEA);
1018+
/* We can safely continue if the lock is inflated or FLC bit is set. */
1019+
if (0 == (lock & (OBJECT_HEADER_LOCK_FLC | OBJECT_HEADER_LOCK_INFLATED))) {
1020+
goto restart;
1021+
}
1022+
}
1023+
}
1024+
}
9331025
}
1026+
1027+
/* Walk all owned monitors and detach from current carrier thread. */
9341028
if (currentThread->ownedMonitorCount > 0) {
9351029
J9StackWalkState walkState;
9361030

@@ -979,28 +1073,7 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
9791073
}
9801074

9811075
if (NULL != syncObj) {
982-
if (!LN_HAS_LOCKWORD(currentThread, syncObj)) {
983-
syncObjectMonitor = monitorTablePeek(vm, syncObj);
984-
if (NULL != syncObjectMonitor) {
985-
lock = J9_LOAD_LOCKWORD(currentThread, &syncObjectMonitor->alternateLockword);
986-
} else {
987-
lock = 0;
988-
}
989-
} else {
990-
lock = J9OBJECT_MONITOR(currentThread, syncObj);
991-
}
992-
993-
if (!J9_LOCK_IS_INFLATED(lock)) {
994-
syncObjectMonitor = objectMonitorInflate(currentThread, syncObj, lock);
995-
if (NULL == syncObjectMonitor) {
996-
result = J9_OBJECT_MONITOR_OOM;
997-
goto done;
998-
}
999-
} else {
1000-
syncObjectMonitor = J9_INFLLOCK_OBJECT_MONITOR(lock);
1001-
}
1002-
1003-
continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, currentThread->threadObject);
1076+
j9object_t continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, currentThread->threadObject);
10041077
J9VMJDKINTERNALVMCONTINUATION_SET_BLOCKER(currentThread, continuationObj, syncObj);
10051078

10061079
if (isObjectWait) {
@@ -1024,19 +1097,24 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
10241097
currentThread->currentContinuation->objectWaitMonitor = syncObjectMonitor;
10251098
omrthread_monitor_exit(vm->blockedVirtualThreadsMutex);
10261099
} else {
1100+
/* Increment the wait count on inflated monitor. */
10271101
VM_AtomicSupport::addU32(&syncObjectMonitor->virtualThreadWaitCount, 1);
1102+
currentThread->currentContinuation->objectWaitMonitor = syncObjectMonitor;
10281103
}
1029-
1030-
/* Clear the blocking object on the carrier thread. */
1031-
J9VMTHREAD_SET_BLOCKINGENTEROBJECT(currentThread, currentThread, NULL);
10321104
}
10331105

10341106
/* Subtract the detached monitor from the carrier thread's lockedmonitorcount. */
10351107
currentThread->osThread->lockedmonitorcount -= monitorCount;
10361108

10371109
done:
1038-
if ((NULL != syncObj) && (J9_OBJECT_MONITOR_YIELD_VIRTUAL != result)) {
1039-
exitVThreadTransitionCritical(currentThread, (jobject)&currentThread->threadObject);
1110+
if (NULL != syncObj) {
1111+
if (J9_OBJECT_MONITOR_YIELD_VIRTUAL != result) {
1112+
exitVThreadTransitionCritical(currentThread, (jobject)&currentThread->threadObject);
1113+
}
1114+
if (!isObjectWait) {
1115+
/* Clear the blocking object on the carrier thread. */
1116+
J9VMTHREAD_SET_BLOCKINGENTEROBJECT(currentThread, currentThread, NULL);
1117+
}
10401118
}
10411119
return result;
10421120
}

0 commit comments

Comments
 (0)