8376621: Should not suspend thread in start_transition if _is_disable_suspend set

Reviewed-by: dholmes, sspitsyn
This commit is contained in:
Patricio Chilano Mateo 2026-06-01 17:36:17 +00:00
parent d60fead5cf
commit b2bf91bf12
5 changed files with 187 additions and 3 deletions

View File

@ -87,7 +87,8 @@ class UnmountBeginMark : public StackObj {
}
~UnmountBeginMark() {
assert(!_current->is_suspended()
JVMTI_ONLY(|| (_current->is_vthread_transition_disabler() && _result != freeze_ok)), "must be");
JVMTI_ONLY(|| (_result != freeze_ok &&
(_current->is_vthread_transition_disabler() || _current->is_disable_suspend()))), "must be");
assert(_current->is_in_vthread_transition(), "must be");
if (_result != freeze_ok) {

View File

@ -873,7 +873,7 @@ public:
// Atomic version; invoked by a thread other than the owning thread.
bool in_critical_atomic() { return AtomicAccess::load(&_jni_active_critical) > 0; }
bool jni_deferred_suspension() { return AtomicAccess::load(&_jni_deferred_suspension_count); }
bool jni_deferred_suspension() const { return AtomicAccess::load(&_jni_deferred_suspension_count); }
inline void enter_jni_deferred_suspension();
void exit_jni_deferred_suspension() {
precond(Thread::current() == this);

View File

@ -129,7 +129,7 @@ bool MountUnmountDisabler::is_start_transition_disabled(JavaThread* thread, oop
int base_disable_count = notify_jvmti_events() ? 1 : 0;
return java_lang_Thread::vthread_transition_disable_count(vthread) > 0
|| global_vthread_transition_disable_count() > base_disable_count
JVMTI_ONLY(|| (!thread->is_vthread_transition_disabler() &&
JVMTI_ONLY(|| (!thread->is_vthread_transition_disabler() && !thread->is_disable_suspend() &&
(JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended())));
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8376621
* @summary Suspend virtual thread while it's inside disableSuspendAndPreempt region
* @requires vm.continuations
* @requires vm.jvmti
* @library /test/lib /test/hotspot/jtreg/testlibrary
* @run main/othervm SuspendResume4
*/
import jdk.test.lib.Utils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.io.File;
import jvmti.JVMTIUtils;
public class SuspendResume4 {
native static void suspendThread(Thread thread);
native static void resumeThread(Thread thread);
public static void main(String[] args) throws Exception {
// Run test in child VM where Locale won't be initialized already by jtreg
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
"-agentpath:" + Utils.TEST_NATIVE_PATH + File.separator + System.mapLibraryName("SuspendResume4"),
"SuspendResume4$Test");
OutputAnalyzer output = ProcessTools.executeProcess(pb);
System.out.println(output.getStdout());
output.shouldHaveExitValue(0);
}
static class Test{
static String targetName;
private void runTest() throws Exception {
// start target vthread
Thread target = Thread.ofVirtual().name("target").start(() -> {
// Give time for reader to get suspended in
// disableSuspendAndPreempt region.
spinWaitMillis(100);
// Force unmounting. If reader was suspended inside
// disableSuspendAndPreempt region this will block
// in VirtualThread.unmount.
Thread.yield();
});
// start clinit contender
Thread contender = Thread.ofPlatform().name("contender").start(() -> {
"JAVA".toLowerCase(java.util.Locale.ROOT);
});
// start vthread that reads target's state
Thread reader = Thread.ofVirtual().name("reader").start(() -> {
targetName = "name: " + target;
});
// start suspend/resumer
Thread suspender = Thread.ofPlatform().name("suspender").start(() -> {
SuspendResume4.suspendThread(reader);
// Give target time for Thread.yield
spinWaitMillis(100);
SuspendResume4.resumeThread(reader);
});
target.join();
contender.join();
suspender.join();
reader.join();
}
public static void main(String[] args) throws Exception {
Test obj = new Test();
obj.runTest();
}
static void spinWaitMillis(long millis) {
long durationNanos = millis * 1_000_000L;
long start = System.nanoTime();
while (System.nanoTime() - start < durationNanos) {
Thread.onSpinWait();
}
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>
#include "jvmti_common.hpp"
// set by Agent_OnLoad
static jvmtiEnv* jvmti = nullptr;
extern "C" {
JNIEXPORT void JNICALL
Java_SuspendResume4_suspendThread(JNIEnv* jni, jclass klass, jthread thread) {
jvmtiError err = jvmti->SuspendThread(thread);
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_THREAD_NOT_ALIVE) {
jni->FatalError("error in JVMTI SuspendThread");
}
}
JNIEXPORT void JNICALL
Java_SuspendResume4_resumeThread(JNIEnv* jni, jclass klass, jthread thread) {
jvmtiError err = jvmti->ResumeThread(thread);
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_THREAD_NOT_ALIVE) {
jni->FatalError("error in JVMTI ResumeThread");
}
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
jvmtiCapabilities caps;
jvmtiError err;
printf("Agent_OnLoad: started\n");
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
LOG("Agent_OnLoad: error in GetEnv");
return JNI_ERR;
}
memset(&caps, 0, sizeof(caps));
caps.can_suspend = 1;
err = jvmti->AddCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
LOG("Agent_OnLoad: error in JVMTI AddCapabilities: %d\n", err);
}
printf("Agent_OnLoad: finished\n");
return 0;
}
} // extern "C"