Skip to content

Commit afb3132

Browse files
authored
Merge pull request #19229 from fengxue-IS/0.44-19076
(0.44) Refactor virtual thread inspector access
2 parents 8294c69 + e635b0d commit afb3132

15 files changed

+252
-108
lines changed

runtime/j9vm/javanextvmi.cpp

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -274,21 +274,56 @@ enterVThreadTransitionCritical(J9VMThread *currentThread, jobject thread)
274274
MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
275275
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
276276

277-
while(!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0, (U_64)-1)) {
277+
retry:
278+
while (!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0, ~(U_64)0)) {
278279
/* Thread is being inspected or unmounted, wait. */
279280
vmFuncs->internalReleaseVMAccess(currentThread);
280281
VM_AtomicSupport::yieldCPU();
281282
/* After wait, the thread may suspend here. */
282283
vmFuncs->internalAcquireVMAccess(currentThread);
283284
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
284285
}
286+
287+
/* Link the current J9VMThread with the virtual thread object. */
288+
if (!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(currentThread, threadObj, vm->internalSuspendStateOffset, J9_VIRTUALTHREAD_INTERNAL_STATE_NONE, (U_64)currentThread)) {
289+
/* If virtual thread is suspended while unmounted, reset the inspectorCount and do a wait and retry. */
290+
if (VM_VMHelpers::isThreadSuspended(currentThread, threadObj)) {
291+
J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0);
292+
}
293+
vmFuncs->internalReleaseVMAccess(currentThread);
294+
/* Spin is used instead of the halt flag as we cannot guarantee suspend flag is still set now.
295+
*
296+
* TODO: Dynamically increase the sleep time to a bounded maximum.
297+
*/
298+
f_threadSleep(10);
299+
/* After wait, the thread may suspend here. */
300+
vmFuncs->internalAcquireVMAccess(currentThread);
301+
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
302+
goto retry;
303+
}
285304
}
286305

287306
static void
288-
exitVThreadTransitionCritical(J9VMThread *currentThread, j9object_t vthread)
307+
exitVThreadTransitionCritical(J9VMThread *currentThread, jobject thread)
289308
{
290-
Assert_SC_true(-1 == J9OBJECT_I64_LOAD(currentThread, vthread, currentThread->javaVM->virtualThreadInspectorCountOffset));
291-
J9OBJECT_I64_STORE(currentThread, vthread, currentThread->javaVM->virtualThreadInspectorCountOffset, 0);
309+
J9JavaVM *vm = currentThread->javaVM;
310+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
311+
j9object_t vthread = J9_JNI_UNWRAP_REFERENCE(thread);
312+
MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
313+
314+
/* Remove J9VMThread address from internalSuspendedState field, as the thread state is no longer in a transition. */
315+
while (!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(currentThread, vthread, vm->internalSuspendStateOffset, (U_64)currentThread, J9_VIRTUALTHREAD_INTERNAL_STATE_NONE)) {
316+
/* Wait if the suspend flag is set. */
317+
vmFuncs->internalReleaseVMAccess(currentThread);
318+
VM_AtomicSupport::yieldCPU();
319+
/* After wait, the thread may suspend here. */
320+
vmFuncs->internalAcquireVMAccess(currentThread);
321+
vthread = J9_JNI_UNWRAP_REFERENCE(thread);
322+
}
323+
324+
/* Update to virtualThreadInspectorCount must be after clearing isSuspendedInternal field to retain sync ordering. */
325+
Assert_SC_true(-1 == J9OBJECT_I64_LOAD(currentThread, vthread, vm->virtualThreadInspectorCountOffset));
326+
J9OBJECT_I64_STORE(currentThread, vthread, vm->virtualThreadInspectorCountOffset, 0);
292327
}
293328

294329
static void
@@ -301,7 +336,7 @@ setContinuationStateToLastUnmount(J9VMThread *currentThread, jobject thread)
301336
ContinuationState volatile *continuationStatePtr = VM_ContinuationHelpers::getContinuationStateAddress(currentThread, continuationObj);
302337
/* Used in JVMTI to not suspend the virtual thread once it enters the last unmount phase. */
303338
VM_ContinuationHelpers::setLastUnmount(continuationStatePtr);
304-
exitVThreadTransitionCritical(currentThread, threadObj);
339+
exitVThreadTransitionCritical(currentThread, thread);
305340
}
306341

307342
/* Caller must have VMAccess. */
@@ -329,27 +364,6 @@ virtualThreadMountBegin(JNIEnv *env, jobject thread)
329364

330365
enterVThreadTransitionCritical(currentThread, thread);
331366

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

@@ -378,7 +392,7 @@ virtualThreadMountEnd(JNIEnv *env, jobject thread)
378392
VM_VMHelpers::virtualThreadHideFrames(currentThread, JNI_FALSE);
379393

380394
/* Allow thread to be inspected again. */
381-
exitVThreadTransitionCritical(currentThread, threadObj);
395+
exitVThreadTransitionCritical(currentThread, thread);
382396

383397
TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_MOUNT(vm->hookInterface, currentThread);
384398
}
@@ -412,13 +426,12 @@ virtualThreadUnmountBegin(JNIEnv *env, jobject thread)
412426

413427
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
414428
j9object_t carrierThreadObject = currentThread->carrierThreadObject;
415-
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
416429
/* Virtual thread is being umounted. If its carrier thread is suspended, spin until
417430
* the carrier thread is resumed. The carrier thread should not be mounted until it
418431
* is resumed.
419432
*/
420-
while (0 != J9OBJECT_U32_LOAD(currentThread, carrierThreadObject, vm->isSuspendedInternalOffset)) {
421-
exitVThreadTransitionCritical(currentThread, threadObj);
433+
while (VM_VMHelpers::isThreadSuspended(currentThread, carrierThreadObject)) {
434+
exitVThreadTransitionCritical(currentThread, thread);
422435
vmFuncs->internalReleaseVMAccess(currentThread);
423436
/* Spin is used instead of the halt flag; otherwise, the virtual thread will
424437
* show as suspended.
@@ -429,7 +442,6 @@ virtualThreadUnmountBegin(JNIEnv *env, jobject thread)
429442
vmFuncs->internalAcquireVMAccess(currentThread);
430443
enterVThreadTransitionCritical(currentThread, thread);
431444
carrierThreadObject = currentThread->carrierThreadObject;
432-
threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
433445
}
434446

435447
VM_VMHelpers::virtualThreadHideFrames(currentThread, JNI_TRUE);
@@ -467,7 +479,7 @@ virtualThreadUnmountEnd(JNIEnv *env, jobject thread)
467479
VM_VMHelpers::virtualThreadHideFrames(currentThread, JNI_FALSE);
468480

469481
/* Allow thread to be inspected again. */
470-
exitVThreadTransitionCritical(currentThread, threadObj);
482+
exitVThreadTransitionCritical(currentThread, thread);
471483
}
472484
#endif /* JAVA_SPEC_VERSION >= 19 */
473485

@@ -609,7 +621,7 @@ JVM_VirtualThreadHideFrames(
609621

610622
if (!hide) {
611623
Assert_SC_true(hiddenFrames);
612-
exitVThreadTransitionCritical(currentThread, vThreadObj);
624+
exitVThreadTransitionCritical(currentThread, (jobject)&currentThread->threadObject);
613625
}
614626

615627
vmFuncs->internalExitVMToJNI(currentThread);

runtime/jcl/common/jclcinit.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,8 @@ initializeRequiredClasses(J9VMThread *vmThread, char* dllName)
655655
return 1;
656656
}
657657

658-
/* Stores a non-zero value if the virtual or carrier thread is suspended by JVMTI. */
659-
if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/Thread", "isSuspendedInternal", "I", &vm->isSuspendedInternalOffset)) {
658+
/* Stores the carrier J9VMThread if thread is in transition, and bit flags for suspend state in the last 8 bits. */
659+
if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/Thread", "internalSuspendState", "J", &vm->internalSuspendStateOffset)) {
660660
return 1;
661661
}
662662
#endif /* JAVA_SPEC_VERSION >= 19 */

runtime/jcl/common/thread.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ Java_java_lang_Thread_resumeImpl(JNIEnv *env, jobject rcv)
213213
vmFuncs->clearHaltFlag(targetThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
214214
}
215215
#if JAVA_SPEC_VERSION >= 19
216-
J9OBJECT_U32_STORE(currentThread, receiverObject, vm->isSuspendedInternalOffset, 0);
216+
J9OBJECT_U64_STORE(currentThread, receiverObject, vm->internalSuspendStateOffset, J9_VIRTUALTHREAD_INTERNAL_STATE_NONE);
217217
#endif /* JAVA_SPEC_VERSION >= 19 */
218218
}
219219
}
@@ -234,7 +234,7 @@ Java_java_lang_Thread_suspendImpl(JNIEnv *env, jobject rcv)
234234
if (J9VMJAVALANGTHREAD_STARTED(currentThread, receiverObject)) {
235235
if (NULL != targetThread) {
236236
#if JAVA_SPEC_VERSION >= 19
237-
J9OBJECT_U32_STORE(currentThread, receiverObject, vm->isSuspendedInternalOffset, 1);
237+
J9OBJECT_U64_STORE(currentThread, receiverObject, vm->internalSuspendStateOffset, J9_VIRTUALTHREAD_INTERNAL_STATE_SUSPENDED);
238238
if (receiverObject == targetThread->threadObject)
239239
#endif /* JAVA_SPEC_VERSION >= 19 */
240240
{

runtime/jvmti/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,21 @@ j9vm_add_library(j9jvmti SHARED
3232
jvmtiEventManagement.c
3333
jvmtiExtensionMechanism.c
3434
jvmtiField.c
35-
jvmtiForceEarlyReturn.c
35+
jvmtiForceEarlyReturn.cpp
3636
jvmtiFunctionTable.c
3737
jvmtiGeneral.c
3838
jvmtiHeap.c
3939
jvmtiHeap10.c
4040
jvmtiHelpers.cpp
4141
jvmtiHook.c
4242
jvmtiJNIFunctionInterception.c
43-
jvmtiLocalVariable.c
43+
jvmtiLocalVariable.cpp
4444
jvmtiMemory.c
4545
jvmtiMethod.c
4646
jvmtiModules.c
4747
jvmtiObject.c
4848
jvmtiRawMonitor.c
49-
jvmtiStackFrame.c
49+
jvmtiStackFrame.cpp
5050
jvmtiStartup.c
5151
jvmtiSystemProperties.c
5252
jvmtiThread.cpp

runtime/jvmti/jvmtiForceEarlyReturn.c renamed to runtime/jvmti/jvmtiForceEarlyReturn.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
#include "jvmtiHelpers.h"
2424
#include "jvmti_internal.h"
2525

26+
#if JAVA_SPEC_VERSION >= 19
27+
#include "VMHelpers.hpp"
28+
#endif /* JAVA_SPEC_VERSION >= 19 */
29+
30+
extern "C" {
31+
2632
static jvmtiError JNICALL jvmtiForceEarlyReturn(jvmtiEnv* env, jthread thread, jvmtiParamTypes returnValueType, void *value);
2733

2834
jvmtiError JNICALL
@@ -163,7 +169,7 @@ jvmtiForceEarlyReturn(jvmtiEnv* env,
163169

164170
if ((currentThread != targetThread)
165171
#if JAVA_SPEC_VERSION >= 21
166-
&& (0 == J9OBJECT_U32_LOAD(currentThread, threadObject, vm->isSuspendedInternalOffset))
172+
&& (!VM_VMHelpers::isThreadSuspended(currentThread, threadObject))
167173
#else /* JAVA_SPEC_VERSION >= 21 */
168174
&& OMR_ARE_NO_BITS_SET(targetThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND)
169175
#endif /* JAVA_SPEC_VERSION >= 21 */
@@ -182,23 +188,23 @@ jvmtiForceEarlyReturn(jvmtiEnv* env,
182188
threadToWalk = &stackThread;
183189
}
184190
#endif /* JAVA_SPEC_VERSION >= 21 */
185-
rc = findDecompileInfo(currentThread, threadToWalk, 0, &walkState);
186-
if (JVMTI_ERROR_NONE == rc) {
187-
J9Method *method = walkState.userData3;
188-
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
189-
if (romMethod->modifiers & J9AccNative) {
190-
rc = JVMTI_ERROR_OPAQUE_FRAME;
191-
} else {
192-
J9UTF8 *sig = J9ROMMETHOD_SIGNATURE(romMethod);
193-
U_8 *data = J9UTF8_DATA(sig);
194-
U_16 length = J9UTF8_LENGTH(sig);
195-
char signatureType = data[length - 1];
196-
jvmtiParamTypes methodReturnType = JVMTI_TYPE_CVOID;
197-
198-
if (('[' == data[length - 2]) || (';' == signatureType)) {
199-
signatureType = 'L';
200-
}
201-
switch(signatureType) {
191+
rc = (jvmtiError)(IDATA)findDecompileInfo(currentThread, threadToWalk, 0, &walkState);
192+
if (JVMTI_ERROR_NONE == rc) {
193+
J9Method *method = (J9Method *)walkState.userData3;
194+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
195+
if (J9_ARE_ANY_BITS_SET(romMethod->modifiers, J9AccNative)) {
196+
rc = JVMTI_ERROR_OPAQUE_FRAME;
197+
} else {
198+
J9UTF8 *sig = J9ROMMETHOD_SIGNATURE(romMethod);
199+
U_8 *data = J9UTF8_DATA(sig);
200+
U_16 length = J9UTF8_LENGTH(sig);
201+
char signatureType = data[length - 1];
202+
jvmtiParamTypes methodReturnType = JVMTI_TYPE_CVOID;
203+
204+
if (('[' == data[length - 2]) || (';' == signatureType)) {
205+
signatureType = 'L';
206+
}
207+
switch(signatureType) {
202208
case 'I':
203209
case 'B':
204210
case 'C':
@@ -265,3 +271,5 @@ jvmtiForceEarlyReturn(jvmtiEnv* env,
265271

266272
return rc;
267273
}
274+
275+
} /* extern "C" */

runtime/jvmti/jvmtiHelpers.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#if JAVA_SPEC_VERSION >= 19
2828
#include "HeapIteratorAPI.h"
2929
#include "ContinuationHelpers.hpp"
30+
#include "VMHelpers.hpp"
3031
#endif /* JAVA_SPEC_VERSION >= 19 */
3132

3233
extern "C" {
@@ -168,6 +169,13 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr,
168169
if (NULL != carrierThread) {
169170
targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, carrierThread);
170171
}
172+
if (J9OBJECT_I64_LOAD(currentThread, threadObject, vm->virtualThreadInspectorCountOffset) < -1) {
173+
/* If the virtual thread is suspended in transition, check if the mounting process is already completed. */
174+
J9VMThread *carrierVMThread = VM_VMHelpers::getCarrierVMThread(currentThread, threadObject);
175+
if (NULL != carrierVMThread->currentContinuation) {
176+
targetThread = carrierVMThread;
177+
}
178+
}
171179
isThreadAlive = (JVMTI_VTHREAD_STATE_NEW != vthreadState) && (JVMTI_VTHREAD_STATE_TERMINATED != vthreadState);
172180
} else
173181
#endif /* JAVA_SPEC_VERSION >= 19 */
@@ -740,10 +748,10 @@ getThreadStateHelper(J9VMThread *currentThread, j9object_t threadObject, J9VMThr
740748
state |= JVMTI_THREAD_STATE_SUSPENDED;
741749
}
742750
#if JAVA_SPEC_VERSION >= 19
743-
/* Based on the isSuspendedInternal field, set the JVMTI
751+
/* Based on the internalSuspendState field, set the JVMTI
744752
* thread state to suspended for the corresponding thread.
745753
*/
746-
if (0 != J9OBJECT_U32_LOAD(currentThread, threadObject, currentThread->javaVM->isSuspendedInternalOffset)) {
754+
if (VM_VMHelpers::isThreadSuspended(currentThread, threadObject)) {
747755
state |= JVMTI_THREAD_STATE_SUSPENDED;
748756
} else {
749757
state &= ~JVMTI_THREAD_STATE_SUSPENDED;
@@ -884,9 +892,9 @@ getVirtualThreadState(J9VMThread *currentThread, jthread thread)
884892
rc = JVMTI_ERROR_INTERNAL;
885893
}
886894
}
887-
/* Re-fetch object to correctly set the isSuspendedInternal field. */
895+
/* Re-fetch object to correctly set the internalSuspendState field. */
888896
vThreadObject = J9_JNI_UNWRAP_REFERENCE(thread);
889-
if (0 != J9OBJECT_U32_LOAD(currentThread, vThreadObject, vm->isSuspendedInternalOffset)) {
897+
if (VM_VMHelpers::isThreadSuspended(currentThread, vThreadObject)) {
890898
rc |= JVMTI_THREAD_STATE_SUSPENDED;
891899
} else {
892900
rc &= ~JVMTI_THREAD_STATE_SUSPENDED;

runtime/jvmti/jvmtiLocalVariable.c renamed to runtime/jvmti/jvmtiLocalVariable.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
#include "jvmti_internal.h"
2525
#include "objhelp.h"
2626

27+
#if JAVA_SPEC_VERSION >= 19
28+
#include "VMHelpers.hpp"
29+
#endif /* JAVA_SPEC_VERSION >= 19 */
30+
31+
extern "C" {
32+
2733
static jvmtiError jvmtiGetOrSetLocal (jvmtiEnv* env, jthread thread, jint depth, jint slot, void* value_ptr, char signature, jboolean isSet, jboolean getLocalInstance);
2834

2935

@@ -327,7 +333,7 @@ jvmtiGetOrSetLocal(jvmtiEnv *env,
327333

328334
#if JAVA_SPEC_VERSION >= 20
329335
if ((currentThread != targetThread)
330-
&& (0 == J9OBJECT_U32_LOAD(currentThread, threadObject, vm->isSuspendedInternalOffset))
336+
&& (!VM_VMHelpers::isThreadSuspended(currentThread, threadObject))
331337
) {
332338
rc = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
333339
goto release;
@@ -350,12 +356,12 @@ jvmtiGetOrSetLocal(jvmtiEnv *env,
350356
}
351357
#endif /* JAVA_SPEC_VERSION < 20 */
352358

353-
rc = findDecompileInfo(currentThread, threadToWalk, (UDATA)depth, &walkState);
359+
rc = (jvmtiError)(IDATA)findDecompileInfo(currentThread, threadToWalk, (UDATA)depth, &walkState);
354360
if (JVMTI_ERROR_NONE == rc) {
355361
UDATA validateRC = 0;
356362
BOOLEAN slotValid = TRUE;
357363
UDATA *slotAddress = NULL;
358-
J9Method *ramMethod = walkState.userData3;
364+
J9Method *ramMethod = (J9Method *)walkState.userData3;
359365
U_32 offsetPC = (U_32)(UDATA)walkState.userData4;
360366
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(ramMethod);
361367

@@ -484,3 +490,5 @@ jvmtiGetOrSetLocal(jvmtiEnv *env,
484490
}
485491

486492
#endif /* J9VM_OPT_DEBUG_INFO_SERVER */
493+
494+
} /* extern "C" */

0 commit comments

Comments
 (0)