RFR: 8316397: StackTrace/Suspended/GetStackTraceSuspendedStressTest.java failed with: SingleStep event is NOT expected [v4]

David Holmes dholmes at openjdk.org
Tue Apr 8 23:02:33 UTC 2025


On Thu, 3 Apr 2025 17:55:16 GMT, Patricio Chilano Mateo <pchilanomate at openjdk.org> wrote:

>> David, we had an internal Zoom meeting with Patricio and agreed on a couple of points (I hope, Patricio will fix me if needed).
>> #1:
>> The main (biggest) problem is the call to `JvmtiEventController::thread_started()` in the function `get_jvmti_thread_state()`. The function `JvmtiThreadState::state_for()` is called there if a mounted virtual thread is the current thread and any thread-filtered event is enabled globally. The function `JvmtiThreadState::state_for()` has this line: `MutexLocker mu(JvmtiThreadState_lock);`
>> 
>> The `JvmtiThreadState_lock` allows safepointing. It is where a JVMTI suspend request for this thread has been processed/registered. The `SingleStep` events for this thread are also enabled after the suspend request was processed/registered. However, suspends are not allowed by this `MutexLocker`.
>> This is very common code path for any thread-filtered event (e.g. the event can be enabled for a specific thread).
>> 
>> #2:
>> This PR fixes this exact issue with this update:
>> 
>> JvmtiThreadState* JvmtiExport::get_jvmti_thread_state(JavaThread *thread) {
>>   assert(thread == JavaThread::current(), "must be current thread");
>>   if (thread->is_vthread_mounted() && thread->jvmti_thread_state() == nullptr) {
>>     JvmtiEventController::thread_started(thread);
>> +    if (thread->is_suspended()) {
>> +      // suspend here if there is a suspend request
>> +      ThreadBlockInVM tbivm(thread);
>> +    }
>>   }
>>   . . .
>> 
>> 
>> But we also agreed to add a tweak for the function `post_dynamic_code_generated_while_holding_locks()` to disallow suspension in this context. Then it will look like this:
>> 
>> +JvmtiThreadState* JvmtiExport::get_jvmti_thread_state(JavaThread *thread, bool allow_suspend) {
>>   assert(thread == JavaThread::current(), "must be current thread");
>>   if (thread->is_vthread_mounted() && thread->jvmti_thread_state() == nullptr) {
>>     JvmtiEventController::thread_started(thread);
>> +    if (allow_suspend && thread->is_suspended()) {
>> +      // suspend here if there is a suspend request
>> +      ThreadBlockInVM tbivm(thread, true /* allow suspend */);
>> +    }
>>   }
>>   . . .
>> 
>> void JvmtiExport::post_dynamic_code_generated_while_holding_locks(const char* name,
>>                                                                   address code_begin, address code_end)
>> {
>>   . . .
>>   JvmtiThreadState *state = get_jvmti_thread_state(thread, false /* allow_suspend */);
>>   . . .
>> 
>> 
>> I have already pushed the minor tweak above....
>
> Thanks for the summary Serguei. I would only add that IMO, given that it’s already clear there could be many suspend points before even reaching one of this `JvmtiExport::post_*` methods (like the one shown in #3), I would just address all these cases here too my moving the suspend check outside of the `thread->is_vthread_mounted() && thread->jvmti_thread_state() == nullptr` conditional. But since this PR has already been blocked for too long I’m okay if we only want to address the specific suspend point in #1.

I thought we already guarded/handled suspension whilst acquiring a mutex, as a general case. ??? Now I am getting really confused about the state of the world.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/23490#discussion_r2034142625


More information about the hotspot-runtime-dev mailing list