Skip to content

Commit d2811ca

Browse files
committed
Fix issue with waiting vthreads
When a vthread is in a timed wait, it is added to the running queue by a Java thread directly if the timeout elapses. In this case, we need to ensure it is removed from any waiting list, as it will not pass through the takeVirtualThreadListToUnblock path. Signed-off-by: tajila <atobia@ca.ibm.com>
1 parent eac2531 commit d2811ca

File tree

4 files changed

+102
-28
lines changed

4 files changed

+102
-28
lines changed

runtime/oti/ContinuationHelpers.hpp

+33
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,39 @@ class VM_ContinuationHelpers {
290290
&& (0 == vmThread->continuationPinCount)
291291
&& (0 == vmThread->callOutCount));
292292
}
293+
294+
/**
295+
* Remove a continuation from the provided list.
296+
*
297+
* @param[in] list the list from which the continuation should be removed
298+
* @param[in] continuation the continuation to be removed
299+
*
300+
* @return true if the continuation is found and removed from the list, otherwise false
301+
*/
302+
static bool
303+
removeContinuationFromList(J9VMContinuation **list, J9VMContinuation *continuation)
304+
{
305+
bool foundInList = false;
306+
J9VMContinuation *previous = NULL;
307+
J9VMContinuation *current = *list;
308+
309+
while (NULL != current) {
310+
if (continuation == current) {
311+
foundInList = true;
312+
if (NULL == previous) {
313+
*list = current->nextWaitingContinuation;
314+
} else {
315+
previous->nextWaitingContinuation = current->nextWaitingContinuation;
316+
}
317+
current->nextWaitingContinuation = NULL;
318+
break;
319+
}
320+
previous = current;
321+
current = current->nextWaitingContinuation;
322+
}
323+
324+
return foundInList;
325+
}
293326
#endif /* JAVA_SPEC_VERSION >= 24 */
294327
};
295328

runtime/oti/j9nonbuilder.h

+1
Original file line numberDiff line numberDiff line change
@@ -5462,6 +5462,7 @@ typedef struct J9VMContinuation {
54625462
struct J9MonitorEnterRecord* jniMonitorEnterRecords;
54635463
j9object_t vthread;
54645464
struct J9VMContinuation* nextWaitingContinuation;
5465+
struct J9ObjectMonitor* objectWaitMonitor;
54655466
#endif /* JAVA_SPEC_VERSION >= 24 */
54665467
} J9VMContinuation;
54675468
#endif /* JAVA_SPEC_VERSION >= 19 */

runtime/vm/BytecodeInterpreter.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -2952,14 +2952,14 @@ class INTERPRETER_CLASS
29522952
J9VMJAVALANGVIRTUALTHREAD_SET_NOTIFIED(_currentThread, head->vthread, JNI_TRUE);
29532953
} else {
29542954
J9VMContinuation *next = head;
2955-
J9VMJAVALANGVIRTUALTHREAD_SET_ONWAITINGLIST(_currentThread, head->vthread, JNI_TRUE);
2956-
J9VMJAVALANGVIRTUALTHREAD_SET_NOTIFIED(_currentThread, head->vthread, JNI_TRUE);
2957-
while (NULL != next->nextWaitingContinuation) {
2955+
J9VMContinuation *prev = NULL;
2956+
while (NULL != next) {
29582957
J9VMJAVALANGVIRTUALTHREAD_SET_ONWAITINGLIST(_currentThread, next->vthread, JNI_TRUE);
29592958
J9VMJAVALANGVIRTUALTHREAD_SET_NOTIFIED(_currentThread, next->vthread, JNI_TRUE);
2959+
prev = next;
29602960
next = next->nextWaitingContinuation;
29612961
}
2962-
next->nextWaitingContinuation = _vm->blockedContinuations;
2962+
prev->nextWaitingContinuation = _vm->blockedContinuations;
29632963
_vm->blockedContinuations = head;
29642964
objectMonitor->waitingContinuations = NULL;
29652965
}
@@ -5174,7 +5174,7 @@ class INTERPRETER_CLASS
51745174
VMStructHasBeenUpdated(REGISTER_ARGS);
51755175
if (J9_OBJECT_MONITOR_OOM != result) {
51765176
restoreInternalNativeStackFrame(REGISTER_ARGS);
5177-
/* Handle the virutal thread Object.wait call. */
5177+
/* Handle the virtual thread Object.wait call. */
51785178
J9VMJAVALANGVIRTUALTHREAD_SET_NOTIFIED(_currentThread, _currentThread->threadObject, JNI_FALSE);
51795179
rc = yieldPinnedContinuation(REGISTER_ARGS, newState, J9VM_CONTINUATION_RETURN_FROM_OBJECT_WAIT);
51805180
} else {

runtime/vm/ContinuationHelpers.cpp

+63-23
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ synchronizeWithConcurrentGCScan(J9VMThread *currentThread, j9object_t continuati
220220
BOOLEAN
221221
enterContinuation(J9VMThread *currentThread, j9object_t continuationObject)
222222
{
223+
J9JavaVM *vm = currentThread->javaVM;
223224
BOOLEAN result = TRUE;
224225
J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObject);
225226
ContinuationState volatile *continuationStatePtr = VM_ContinuationHelpers::getContinuationStateAddress(currentThread, continuationObject);
@@ -232,7 +233,7 @@ enterContinuation(J9VMThread *currentThread, j9object_t continuationObject)
232233
/* Directly return result if the create code failed, exception is already set. */
233234
return result;
234235
}
235-
currentThread->javaVM->memoryManagerFunctions->continuationObjectStarted(currentThread, continuationObject);
236+
vm->memoryManagerFunctions->continuationObjectStarted(currentThread, continuationObject);
236237

237238
continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObject);
238239
#if JAVA_SPEC_VERSION >= 24
@@ -241,6 +242,33 @@ enterContinuation(J9VMThread *currentThread, j9object_t continuationObject)
241242
}
242243
Assert_VM_notNull(continuation);
243244

245+
#if JAVA_SPEC_VERSION >= 24
246+
if (NULL != continuation->nextWaitingContinuation) {
247+
/* Continuation is still in a blocked list. This can happen with TIMED_WAIT.
248+
* It must be removed from the waiting list.
249+
*/
250+
bool foundInBlockedContinuationList = false;
251+
bool foundInMonitorList = false;
252+
253+
omrthread_monitor_enter(vm->blockedVirtualThreadsMutex);
254+
255+
foundInBlockedContinuationList = VM_ContinuationHelpers::removeContinuationFromList(
256+
&vm->blockedContinuations, continuation);
257+
258+
foundInMonitorList = VM_ContinuationHelpers::removeContinuationFromList(
259+
&continuation->objectWaitMonitor->waitingContinuations, continuation);
260+
261+
omrthread_monitor_exit(vm->blockedVirtualThreadsMutex);
262+
263+
Assert_VM_true(foundInMonitorList || foundInMonitorList);
264+
265+
/* Virtual can only be in one list at a time. */
266+
Assert_VM_false(foundInBlockedContinuationList && foundInMonitorList);
267+
268+
continuation->objectWaitMonitor = NULL;
269+
}
270+
#endif /* JAVA_SPEC_VERSION >= 24 */
271+
244272
/* let GC know we are mounting, so they don't need to scan us, or if there is already ongoing scan wait till it's complete. */
245273
continuationObject = synchronizeWithConcurrentGCScan(currentThread, continuationObject, continuationStatePtr);
246274

@@ -816,6 +844,7 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
816844
j9objectmonitor_t lock = 0;
817845
j9object_t continuationObj = NULL;
818846
UDATA monitorCount = 0;
847+
J9JavaVM *vm = currentThread->javaVM;
819848

820849
if (NULL != syncObj) {
821850
enterVThreadTransitionCritical(currentThread, (jobject)&currentThread->threadObject);
@@ -829,7 +858,7 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
829858

830859
if (syncObj != object) {
831860
if (!LN_HAS_LOCKWORD(currentThread, object)) {
832-
objectMonitor = monitorTablePeek(currentThread->javaVM, object);
861+
objectMonitor = monitorTablePeek(vm, object);
833862
if (NULL != objectMonitor) {
834863
lock = J9_LOAD_LOCKWORD(currentThread, &objectMonitor->alternateLockword);
835864
} else {
@@ -863,7 +892,7 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
863892

864893
if (syncObj != object) {
865894
if (!LN_HAS_LOCKWORD(currentThread, object)) {
866-
objectMonitor = monitorTablePeek(currentThread->javaVM, object);
895+
objectMonitor = monitorTablePeek(vm, object);
867896
if (NULL != objectMonitor) {
868897
lock = J9_LOAD_LOCKWORD(currentThread, &objectMonitor->alternateLockword);
869898
} else {
@@ -892,7 +921,7 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
892921

893922
if (NULL != syncObj) {
894923
if (!LN_HAS_LOCKWORD(currentThread, syncObj)) {
895-
syncObjectMonitor = monitorTablePeek(currentThread->javaVM, syncObj);
924+
syncObjectMonitor = monitorTablePeek(vm, syncObj);
896925
if (NULL != syncObjectMonitor) {
897926
lock = J9_LOAD_LOCKWORD(currentThread, &syncObjectMonitor->alternateLockword);
898927
} else {
@@ -930,10 +959,11 @@ preparePinnedVirtualThreadForUnmount(J9VMThread *currentThread, j9object_t syncO
930959

931960
/* Add Continuation struct to the monitor's waiting list. */
932961
omrthread_monitor_exit(monitor);
933-
omrthread_monitor_enter(currentThread->javaVM->blockedVirtualThreadsMutex);
962+
omrthread_monitor_enter(vm->blockedVirtualThreadsMutex);
934963
currentThread->currentContinuation->nextWaitingContinuation = syncObjectMonitor->waitingContinuations;
935964
syncObjectMonitor->waitingContinuations = currentThread->currentContinuation;
936-
omrthread_monitor_exit(currentThread->javaVM->blockedVirtualThreadsMutex);
965+
currentThread->currentContinuation->objectWaitMonitor = syncObjectMonitor;
966+
omrthread_monitor_exit(vm->blockedVirtualThreadsMutex);
937967
} else {
938968
syncObjectMonitor->virtualThreadWaitCount += 1;
939969
}
@@ -971,33 +1001,32 @@ takeVirtualThreadListToUnblock(J9VMThread *currentThread)
9711001
while (NULL == unblockedList) {
9721002
if (NULL != vm->blockedContinuations) {
9731003
restart:
974-
J9VMContinuation *listHead = vm->blockedContinuations;
1004+
J9VMContinuation *previous = NULL;
1005+
J9VMContinuation *current = vm->blockedContinuations;
9751006
J9VMContinuation *next = NULL;
976-
vm->blockedContinuations = NULL;
977-
while (NULL != listHead) {
1007+
while (NULL != current) {
9781008
bool unblocked = false;
979-
next = listHead->nextWaitingContinuation;
980-
U_32 state = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, listHead->vthread);
1009+
U_32 state = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, current->vthread);
1010+
next = current->nextWaitingContinuation;
9811011
/* Skip vthreads that are still in transition. */
9821012
switch (state) {
9831013
case JAVA_LANG_VIRTUALTHREAD_BLOCKING:
9841014
case JAVA_LANG_VIRTUALTHREAD_WAITING:
9851015
case JAVA_LANG_VIRTUALTHREAD_TIMED_WAITING:
986-
listHead->nextWaitingContinuation = vm->blockedContinuations;
987-
vm->blockedContinuations = listHead;
988-
listHead = next;
1016+
previous = current;
1017+
current = next;
9891018
continue;
9901019
case JAVA_LANG_VIRTUALTHREAD_WAIT:
9911020
case JAVA_LANG_VIRTUALTHREAD_TIMED_WAIT:
992-
J9VMJAVALANGVIRTUALTHREAD_SET_STATE(currentThread, listHead->vthread, JAVA_LANG_VIRTUALTHREAD_BLOCKED);
1021+
J9VMJAVALANGVIRTUALTHREAD_SET_STATE(currentThread, current->vthread, JAVA_LANG_VIRTUALTHREAD_BLOCKED);
9931022
/* FALLTHROUGH */
9941023
default:
9951024
break;
9961025
}
997-
if (J9VMJAVALANGVIRTUALTHREAD_ONWAITINGLIST(currentThread, listHead->vthread)) {
1026+
if (J9VMJAVALANGVIRTUALTHREAD_ONWAITINGLIST(currentThread, current->vthread)) {
9981027
unblocked = true;
9991028
} else {
1000-
j9object_t continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, listHead->vthread);
1029+
j9object_t continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, current->vthread);
10011030
j9object_t syncObject = J9VMJDKINTERNALVMCONTINUATION_BLOCKER(currentThread, continuationObj);
10021031
J9ObjectMonitor *syncObjectMonitor = NULL;
10031032
j9objectmonitor_t lock = 0;
@@ -1016,18 +1045,29 @@ takeVirtualThreadListToUnblock(J9VMThread *currentThread)
10161045
if (syncObjectMonitor->virtualThreadWaitCount >= 1) {
10171046
syncObjectMonitor->virtualThreadWaitCount -= 1;
10181047
}
1019-
J9VMJAVALANGVIRTUALTHREAD_SET_ONWAITINGLIST(currentThread, listHead->vthread, JNI_TRUE);
1048+
J9VMJAVALANGVIRTUALTHREAD_SET_ONWAITINGLIST(currentThread, current->vthread, JNI_TRUE);
10201049
}
10211050
}
10221051

10231052
if (unblocked) {
1024-
J9VMJAVALANGVIRTUALTHREAD_SET_NEXT(currentThread, listHead->vthread, unblockedList);
1025-
unblockedList = listHead->vthread;
1053+
/* Add to Java unblock list. */
1054+
J9VMJAVALANGVIRTUALTHREAD_SET_NEXT(currentThread, current->vthread, unblockedList);
1055+
unblockedList = current->vthread;
1056+
1057+
/* Remove from native blocking list. */
1058+
current->nextWaitingContinuation = NULL;
1059+
1060+
if (NULL == previous) {
1061+
vm->blockedContinuations = next;
1062+
} else {
1063+
previous->nextWaitingContinuation = next;
1064+
}
10261065
} else {
1027-
listHead->nextWaitingContinuation = vm->blockedContinuations;
1028-
vm->blockedContinuations = listHead;
1066+
/* Keep in native blocking list. */
1067+
previous = current;
10291068
}
1030-
listHead = next;
1069+
1070+
current = next;
10311071
}
10321072
if (NULL == unblockedList) {
10331073
vmFuncs->internalExitVMToJNI(currentThread);

0 commit comments

Comments
 (0)