Skip to content

Commit feac356

Browse files
committed
Spin during VirtualThread MountBegin and UnmountBegin
Currently, the halt flag is set in VirtualThread MountEnd if a virtual thread is suspended via JVMTI, and in VirtualThread UnmountEnd if a carrier thread is suspended via JVMTI. In the above approach, the halt flag is set too late. As soon as the continuation swaps the J9VMThread context, the thread begins execution and is capable of triggering JVMTI events. To avoid the above issue, the above steps are moved into VirtualThread MountBegin and UnmountBegin. This prevents the continuation to swap the J9VMThread context. Currently, the halt flag is set without invoking exitVThreadTransitionCritical. This prevents JVMTI to resume the halted thread and cause a hang. The new approach spins, invokes exitVThreadTransitionCritical and releases VM access to allow JVMTI to resume the suspended thread. The better approach will be to fail mount if the thread is suspended and retry later. Currently, his approach cannot be implemented because VirtualThread.java does not support this approach. Related: #17865 Related: #17869 Related: #18370 Signed-off-by: Babneet Singh <[email protected]>
1 parent f4501d1 commit feac356

File tree

3 files changed

+52
-21
lines changed

3 files changed

+52
-21
lines changed

runtime/j9vm/javanextvmi.cpp

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ extern "C" {
4040

4141
#if JAVA_SPEC_VERSION >= 19
4242
extern J9JavaVM *BFUjavaVM;
43+
44+
extern IDATA (*f_threadSleep)(I_64 millis);
4345
#endif /* JAVA_SPEC_VERSION >= 19 */
4446

4547
/* Define for debug
@@ -328,6 +330,27 @@ virtualThreadMountBegin(JNIEnv *env, jobject thread)
328330
}
329331

330332
enterVThreadTransitionCritical(currentThread, thread);
333+
334+
/* Virtual thread is being mounted but it has been suspended. Spin until the
335+
* virtual thread is resumed. The virtual thread should not be mounted until
336+
* it is resumed.
337+
*/
338+
J9JavaVM *vm = currentThread->javaVM;
339+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
340+
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
341+
while (0 != J9OBJECT_U32_LOAD(currentThread, threadObj, vm->isSuspendedInternalOffset)) {
342+
exitVThreadTransitionCritical(currentThread, threadObj);
343+
vmFuncs->internalReleaseVMAccess(currentThread);
344+
/* Spin is used instead of the halt flag; otherwise, the carrier thread will
345+
* show as suspended.
346+
*
347+
* TODO: Dynamically increase the sleep time to a bounded maximum.
348+
*/
349+
f_threadSleep(10);
350+
vmFuncs->internalAcquireVMAccess(currentThread);
351+
enterVThreadTransitionCritical(currentThread, thread);
352+
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
353+
}
331354
}
332355

333356
/* Caller must have VMAccess. */
@@ -354,15 +377,6 @@ virtualThreadMountEnd(JNIEnv *env, jobject thread)
354377
J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObj));
355378
}
356379

357-
/* Virtual thread is being mounted but it has been suspended. Thus,
358-
* set J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND flag. At this
359-
* point, virtual thread object is stored in targetThread->threadObject.
360-
*/
361-
if (0 != J9OBJECT_U32_LOAD(currentThread, threadObj, vm->isSuspendedInternalOffset)) {
362-
Assert_SC_true(threadObj == currentThread->threadObject);
363-
vm->internalVMFunctions->setHaltFlag(currentThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
364-
}
365-
366380
/* Allow thread to be inspected again. */
367381
exitVThreadTransitionCritical(currentThread, threadObj);
368382

@@ -396,6 +410,28 @@ virtualThreadUnmountBegin(JNIEnv *env, jobject thread)
396410

397411
enterVThreadTransitionCritical(currentThread, thread);
398412
VM_VMHelpers::virtualThreadHideFrames(currentThread, JNI_TRUE);
413+
414+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
415+
j9object_t carrierThreadObject = currentThread->carrierThreadObject;
416+
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
417+
/* Virtual thread is being umounted. If its carrier thread is suspended, spin until
418+
* the carrier thread is resumed. The carrier thread should not be mounted until it
419+
* is resumed.
420+
*/
421+
while (0 != J9OBJECT_U32_LOAD(currentThread, carrierThreadObject, vm->isSuspendedInternalOffset)) {
422+
exitVThreadTransitionCritical(currentThread, threadObj);
423+
vmFuncs->internalReleaseVMAccess(currentThread);
424+
/* Spin is used instead of the halt flag; otherwise, the virtual thread will
425+
* show as suspended.
426+
*
427+
* TODO: Dynamically increase the sleep time to a bounded maximum.
428+
*/
429+
f_threadSleep(10);
430+
vmFuncs->internalAcquireVMAccess(currentThread);
431+
enterVThreadTransitionCritical(currentThread, thread);
432+
carrierThreadObject = currentThread->carrierThreadObject;
433+
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
434+
}
399435
}
400436

401437
/* Caller must have VMAccess. */
@@ -429,15 +465,6 @@ virtualThreadUnmountEnd(JNIEnv *env, jobject thread)
429465
vmFuncs->freeTLS(currentThread, threadObj);
430466
}
431467

432-
j9object_t carrierThreadObject = currentThread->carrierThreadObject;
433-
/* The J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND will be set for the virtual
434-
* thread's carrier thread if it was suspended while the virtual thread was mounted.
435-
*/
436-
if (0 != J9OBJECT_U32_LOAD(currentThread, carrierThreadObject, vm->isSuspendedInternalOffset)) {
437-
Assert_SC_true((currentThread->threadObject == carrierThreadObject) && (NULL == currentThread->currentContinuation));
438-
vmFuncs->setHaltFlag(currentThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
439-
}
440-
441468
/* Allow thread to be inspected again. */
442469
exitVThreadTransitionCritical(currentThread, threadObj);
443470
}

runtime/j9vm/jvm.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ typedef IDATA (*MonitorDestroy)(omrthread_monitor_t monitor);
212212
typedef IDATA (* ThreadLibControl)(const char * key, UDATA value);
213213
typedef IDATA (*SetCategory)(omrthread_t thread, UDATA category, UDATA type);
214214
typedef IDATA (*LibEnableCPUMonitor)(omrthread_t thread);
215+
typedef IDATA (*ThreadSleep)(I_64 millis);
215216

216217
typedef I_32 (*PortInitLibrary)(J9PortLibrary *portLib, J9PortLibraryVersion *version, UDATA size);
217218
typedef UDATA (*PortGetSize)(struct J9PortLibraryVersion *version);
@@ -260,6 +261,7 @@ static pNewStringPlatform globalNewStringPlatform;
260261
static p_a2e_vsprintf global_a2e_vsprintf;
261262
#endif
262263

264+
ThreadSleep f_threadSleep;
263265
static ThreadGlobal f_threadGlobal;
264266
static ThreadAttachEx f_threadAttachEx;
265267
static ThreadDetach f_threadDetach;
@@ -1015,8 +1017,9 @@ preloadLibraries(void)
10151017
f_threadLibControl = (ThreadLibControl) GetProcAddress (threadDLL, (LPCSTR) "omrthread_lib_control");
10161018
f_setCategory = (SetCategory) GetProcAddress (threadDLL, (LPCSTR) "omrthread_set_category");
10171019
f_libEnableCPUMonitor = (LibEnableCPUMonitor) GetProcAddress (threadDLL, (LPCSTR) "omrthread_lib_enable_cpu_monitor");
1020+
f_threadSleep = (ThreadSleep) GetProcAddress (threadDLL, (LPCSTR) "omrthread_sleep");
10181021
if (!f_threadGlobal || !f_threadAttachEx || !f_threadDetach || !f_monitorEnter || !f_monitorExit || !f_monitorInit
1019-
|| !f_monitorDestroy || !f_threadLibControl || !f_setCategory || !f_libEnableCPUMonitor
1022+
|| !f_monitorDestroy || !f_threadLibControl || !f_setCategory || !f_libEnableCPUMonitor || !f_threadSleep
10201023
) {
10211024
FreeLibrary(vmDLL);
10221025
FreeLibrary(threadDLL);
@@ -1442,8 +1445,9 @@ preloadLibraries(void)
14421445
f_threadLibControl = (ThreadLibControl) dlsym (threadDLL, "omrthread_lib_control");
14431446
f_setCategory = (SetCategory) dlsym (threadDLL, "omrthread_set_category");
14441447
f_libEnableCPUMonitor = (LibEnableCPUMonitor) dlsym (threadDLL, "omrthread_lib_enable_cpu_monitor");
1448+
f_threadSleep = (ThreadSleep) dlsym (threadDLL, "omrthread_sleep");
14451449
if (!f_threadGlobal || !f_threadAttachEx || !f_threadDetach || !f_monitorEnter || !f_monitorExit || !f_monitorInit
1446-
|| !f_monitorDestroy || !f_threadLibControl || !f_setCategory || !f_libEnableCPUMonitor
1450+
|| !f_monitorDestroy || !f_threadLibControl || !f_setCategory || !f_libEnableCPUMonitor || !f_threadSleep
14471451
) {
14481452
dlclose(vmDLL);
14491453
#ifdef J9ZOS390

runtime/jvmti/jvmtiThread.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ jvmtiSuspendResumeCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *o
13261326
{
13271327
j9object_t continuationObj = object->object;
13281328
j9object_t vthread = J9VMJDKINTERNALVMCONTINUATION_VTHREAD(vmThread, continuationObj);
1329-
ContinuationState continuationState = J9VMJDKINTERNALVMCONTINUATION_STATE(vmThread, continuationObj);;
1329+
ContinuationState continuationState = J9VMJDKINTERNALVMCONTINUATION_STATE(vmThread, continuationObj);
13301330

13311331
if ((NULL != vthread) && J9_ARE_NO_BITS_SET(continuationState, J9_GC_CONTINUATION_STATE_LAST_UNMOUNT)) {
13321332
jvmtiVThreadCallBackData *data = (jvmtiVThreadCallBackData*)userData;

0 commit comments

Comments
 (0)