mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-16 17:14:41 +02:00
8257831: Suspend with handshakes
Reviewed-by: dcubed, rrich, dholmes, pchilanomate, sspitsyn
This commit is contained in:
parent
28af31db34
commit
86bd44fe80
40 changed files with 470 additions and 1081 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2018 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
|
@ -440,10 +440,6 @@ void AixAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
// write operation result
|
||||
char msg[32];
|
||||
sprintf(msg, "%d\n", result);
|
||||
|
@ -459,9 +455,6 @@ void AixAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
// done
|
||||
::close(this->socket());
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
@ -472,15 +465,8 @@ AttachOperation* AttachListener::dequeue() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
AttachOperation* op = AixAttachListener::dequeue();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
@ -511,15 +497,8 @@ int AttachListener::pd_init() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
int ret_code = AixAttachListener::init();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, 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
|
||||
|
@ -409,10 +409,6 @@ void BsdAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
// write operation result
|
||||
char msg[32];
|
||||
sprintf(msg, "%d\n", result);
|
||||
|
@ -427,9 +423,6 @@ void BsdAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
// done
|
||||
::close(this->socket());
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
@ -440,15 +433,8 @@ AttachOperation* AttachListener::dequeue() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
AttachOperation* op = BsdAttachListener::dequeue();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
@ -479,15 +465,8 @@ int AttachListener::pd_init() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
int ret_code = BsdAttachListener::init();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, 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
|
||||
|
@ -409,10 +409,6 @@ void LinuxAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
// write operation result
|
||||
char msg[32];
|
||||
sprintf(msg, "%d\n", result);
|
||||
|
@ -427,9 +423,6 @@ void LinuxAttachOperation::complete(jint result, bufferedStream* st) {
|
|||
// done
|
||||
::close(this->socket());
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
@ -440,15 +433,8 @@ AttachOperation* AttachListener::dequeue() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
AttachOperation* op = LinuxAttachListener::dequeue();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
@ -479,15 +465,8 @@ int AttachListener::pd_init() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
int ret_code = LinuxAttachListener::init();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
|
|
|
@ -1664,8 +1664,6 @@ void Parker::park(bool isAbsolute, jlong time) {
|
|||
}
|
||||
|
||||
OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);
|
||||
jt->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
|
||||
|
||||
assert(_cur_index == -1, "invariant");
|
||||
if (time == 0) {
|
||||
|
@ -1688,11 +1686,6 @@ void Parker::park(bool isAbsolute, jlong time) {
|
|||
// Paranoia to ensure our locked and lock-free paths interact
|
||||
// correctly with each other and Java-level accesses.
|
||||
OrderAccess::fence();
|
||||
|
||||
// If externally suspended while waiting, re-suspend
|
||||
if (jt->handle_special_suspend_equivalent_condition()) {
|
||||
jt->java_suspend_self();
|
||||
}
|
||||
}
|
||||
|
||||
void Parker::unpark() {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "runtime/java.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/semaphore.inline.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "signals_posix.hpp"
|
||||
|
@ -369,27 +370,7 @@ static int check_pending_signals() {
|
|||
return i;
|
||||
}
|
||||
}
|
||||
JavaThread *thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
bool threadIsSuspended;
|
||||
do {
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
|
||||
sig_semaphore->wait();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
threadIsSuspended = thread->handle_special_suspend_equivalent_condition();
|
||||
if (threadIsSuspended) {
|
||||
// The semaphore has been incremented, but while we were waiting
|
||||
// another thread suspended us. We don't want to continue running
|
||||
// while suspended because that would surprise the thread that
|
||||
// suspended us.
|
||||
sig_semaphore->signal();
|
||||
|
||||
thread->java_suspend_self();
|
||||
}
|
||||
} while (threadIsSuspended);
|
||||
sig_semaphore->wait_with_safepoint_check(JavaThread::current());
|
||||
}
|
||||
ShouldNotReachHere();
|
||||
return 0; // Satisfy compiler
|
||||
|
@ -1555,10 +1536,6 @@ void PosixSignals::hotspot_sigmask(Thread* thread) {
|
|||
// - sets target osthread state to continue
|
||||
// - sends signal to end the sigsuspend loop in the SR_handler
|
||||
//
|
||||
// Note that the SR_lock plays no role in this suspend/resume protocol,
|
||||
// but is checked for NULL in SR_handler as a thread termination indicator.
|
||||
// The SR_lock is, however, used by JavaThread::java_suspend()/java_resume() APIs.
|
||||
//
|
||||
// Note that resume_clear_context() and suspend_save_context() are needed
|
||||
// by SR_handler(), so that fetch_frame_from_context() works,
|
||||
// which in part is used by:
|
||||
|
@ -1603,11 +1580,11 @@ static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) {
|
|||
|
||||
// On some systems we have seen signal delivery get "stuck" until the signal
|
||||
// mask is changed as part of thread termination. Check that the current thread
|
||||
// has not already terminated (via SR_lock()) - else the following assertion
|
||||
// has not already terminated - else the following assertion
|
||||
// will fail because the thread is no longer a JavaThread as the ~JavaThread
|
||||
// destructor has completed.
|
||||
|
||||
if (thread->SR_lock() == NULL) {
|
||||
if (thread->has_terminated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, 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
|
||||
|
@ -310,10 +310,6 @@ void Win32AttachOperation::complete(jint result, bufferedStream* result_stream)
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
HANDLE hPipe = open_pipe();
|
||||
int lastError = (int)::GetLastError();
|
||||
if (hPipe != INVALID_HANDLE_VALUE) {
|
||||
|
@ -351,9 +347,6 @@ void Win32AttachOperation::complete(jint result, bufferedStream* result_stream)
|
|||
|
||||
::ReleaseMutex(Win32AttachListener::mutex());
|
||||
}
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
}
|
||||
|
||||
|
||||
|
@ -363,15 +356,8 @@ AttachOperation* AttachListener::dequeue() {
|
|||
JavaThread* thread = JavaThread::current();
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
AttachOperation* op = Win32AttachListener::dequeue();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
thread->check_and_wait_while_suspended();
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
|
|
@ -2207,28 +2207,7 @@ static int check_pending_signals() {
|
|||
return i;
|
||||
}
|
||||
}
|
||||
JavaThread *thread = JavaThread::current();
|
||||
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
|
||||
bool threadIsSuspended;
|
||||
do {
|
||||
thread->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
|
||||
sig_sem->wait();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
threadIsSuspended = thread->handle_special_suspend_equivalent_condition();
|
||||
if (threadIsSuspended) {
|
||||
// The semaphore has been incremented, but while we were waiting
|
||||
// another thread suspended us. We don't want to continue running
|
||||
// while suspended because that would surprise the thread that
|
||||
// suspended us.
|
||||
sig_sem->signal();
|
||||
|
||||
thread->java_suspend_self();
|
||||
}
|
||||
} while (threadIsSuspended);
|
||||
sig_sem->wait_with_safepoint_check(JavaThread::current());
|
||||
}
|
||||
ShouldNotReachHere();
|
||||
return 0; // Satisfy compiler
|
||||
|
@ -5487,15 +5466,9 @@ void Parker::park(bool isAbsolute, jlong time) {
|
|||
} else {
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
|
||||
thread->set_suspend_equivalent();
|
||||
|
||||
WaitForSingleObject(_ParkHandle, time);
|
||||
ResetEvent(_ParkHandle);
|
||||
|
||||
// If externally suspended while waiting, re-suspend
|
||||
if (thread->handle_special_suspend_equivalent_condition()) {
|
||||
thread->java_suspend_self();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@
|
|||
LOG_TAG(stringtable) \
|
||||
LOG_TAG(subclass) \
|
||||
LOG_TAG(survivor) \
|
||||
LOG_TAG(suspend) \
|
||||
LOG_TAG(sweep) \
|
||||
LOG_TAG(symboltable) \
|
||||
LOG_TAG(system) \
|
||||
|
|
|
@ -2980,32 +2980,9 @@ JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
|
|||
JavaThread* receiver = NULL;
|
||||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
{
|
||||
MutexLocker ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (receiver->is_external_suspend()) {
|
||||
// Don't allow nested external suspend requests. We can't return
|
||||
// an error from this interface so just ignore the problem.
|
||||
return;
|
||||
}
|
||||
if (receiver->is_exiting()) { // thread is in the process of exiting
|
||||
return;
|
||||
}
|
||||
receiver->set_external_suspend();
|
||||
}
|
||||
|
||||
// java_suspend() will catch threads in the process of exiting
|
||||
// and will ignore them.
|
||||
// jthread refers to a live JavaThread, but java_suspend() will
|
||||
// detect a thread that has started to exit and will ignore it.
|
||||
receiver->java_suspend();
|
||||
|
||||
// It would be nice to have the following assertion in all the
|
||||
// time, but it is possible for a racing resume request to have
|
||||
// resumed this thread right after we suspended it. Temporarily
|
||||
// enable this assertion if you are chasing a different kind of
|
||||
// bug.
|
||||
//
|
||||
// assert(java_lang_Thread::thread(receiver->threadObj()) == NULL ||
|
||||
// receiver->is_being_ext_suspended(), "thread is not suspended");
|
||||
}
|
||||
JVM_END
|
||||
|
||||
|
@ -3016,22 +2993,6 @@ JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
|
|||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
|
||||
// This is the original comment for this Threads_lock grab:
|
||||
// We need to *always* get the threads lock here, since this operation cannot be allowed during
|
||||
// a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
|
||||
// threads randomly resumes threads, then a thread might not be suspended when the safepoint code
|
||||
// looks at it.
|
||||
//
|
||||
// The above comment dates back to when we had both internal and
|
||||
// external suspend APIs that shared a common underlying mechanism.
|
||||
// External suspend is now entirely cooperative and doesn't share
|
||||
// anything with internal suspend. That said, there are some
|
||||
// assumptions in the VM that an external resume grabs the
|
||||
// Threads_lock. We can't drop the Threads_lock grab here until we
|
||||
// resolve the assumptions that exist elsewhere.
|
||||
//
|
||||
MutexLocker ml(Threads_lock);
|
||||
receiver->java_resume();
|
||||
}
|
||||
JVM_END
|
||||
|
|
|
@ -876,7 +876,7 @@ JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
|
|||
// We have a JavaThread* so add more state bits.
|
||||
JavaThreadState jts = java_thread->thread_state();
|
||||
|
||||
if (java_thread->is_being_ext_suspended()) {
|
||||
if (java_thread->is_suspended()) {
|
||||
state |= JVMTI_THREAD_STATE_SUSPENDED;
|
||||
}
|
||||
if (jts == _thread_in_native) {
|
||||
|
@ -942,24 +942,18 @@ jvmtiError
|
|||
JvmtiEnv::SuspendThread(JavaThread* java_thread) {
|
||||
// don't allow hidden thread suspend request.
|
||||
if (java_thread->is_hidden_from_external_view()) {
|
||||
return (JVMTI_ERROR_NONE);
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker ml(java_thread->SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (java_thread->is_external_suspend()) {
|
||||
// don't allow nested external suspend requests.
|
||||
return (JVMTI_ERROR_THREAD_SUSPENDED);
|
||||
if (java_thread->is_suspended()) {
|
||||
return JVMTI_ERROR_THREAD_SUSPENDED;
|
||||
}
|
||||
if (java_thread->is_exiting()) { // thread is in the process of exiting
|
||||
return (JVMTI_ERROR_THREAD_NOT_ALIVE);
|
||||
}
|
||||
java_thread->set_external_suspend();
|
||||
}
|
||||
|
||||
if (!JvmtiSuspendControl::suspend(java_thread)) {
|
||||
// the thread was in the process of exiting
|
||||
return (JVMTI_ERROR_THREAD_NOT_ALIVE);
|
||||
// Either the thread is already suspended or
|
||||
// it was in the process of exiting.
|
||||
if (java_thread->is_exiting()) {
|
||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
}
|
||||
return JVMTI_ERROR_THREAD_SUSPENDED;
|
||||
}
|
||||
return JVMTI_ERROR_NONE;
|
||||
} /* end SuspendThread */
|
||||
|
@ -970,8 +964,10 @@ JvmtiEnv::SuspendThread(JavaThread* java_thread) {
|
|||
// results - pre-checked for NULL
|
||||
jvmtiError
|
||||
JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
|
||||
int self_index = -1;
|
||||
int needSafepoint = 0; // > 0 if we need a safepoint
|
||||
ThreadsListHandle tlh;
|
||||
JavaThread* current = JavaThread::current();
|
||||
ThreadsListHandle tlh(current);
|
||||
for (int i = 0; i < request_count; i++) {
|
||||
JavaThread *java_thread = NULL;
|
||||
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
|
||||
|
@ -984,38 +980,38 @@ JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvm
|
|||
results[i] = JVMTI_ERROR_NONE; // indicate successful suspend
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker ml(java_thread->SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (java_thread->is_external_suspend()) {
|
||||
// don't allow nested external suspend requests.
|
||||
if (java_thread->is_suspended()) {
|
||||
results[i] = JVMTI_ERROR_THREAD_SUSPENDED;
|
||||
continue;
|
||||
}
|
||||
if (java_thread->is_exiting()) { // thread is in the process of exiting
|
||||
results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
if (java_thread == current) {
|
||||
self_index = i;
|
||||
continue;
|
||||
}
|
||||
java_thread->set_external_suspend();
|
||||
}
|
||||
if (java_thread->thread_state() == _thread_in_native) {
|
||||
// We need to try and suspend native threads here. Threads in
|
||||
// other states will self-suspend on their next transition.
|
||||
if (!JvmtiSuspendControl::suspend(java_thread)) {
|
||||
// The thread was in the process of exiting. Force another
|
||||
// safepoint to make sure that this thread transitions.
|
||||
needSafepoint++;
|
||||
// Either the thread is already suspended or
|
||||
// it was in the process of exiting.
|
||||
if (java_thread->is_exiting()) {
|
||||
results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
needSafepoint++;
|
||||
results[i] = JVMTI_ERROR_THREAD_SUSPENDED;
|
||||
continue;
|
||||
}
|
||||
results[i] = JVMTI_ERROR_NONE; // indicate successful suspend
|
||||
}
|
||||
if (needSafepoint > 0) {
|
||||
VM_ThreadsSuspendJVMTI tsj;
|
||||
VMThread::execute(&tsj);
|
||||
if (self_index >= 0) {
|
||||
if (!JvmtiSuspendControl::suspend(current)) {
|
||||
// Either the thread is already suspended or
|
||||
// it was in the process of exiting.
|
||||
if (current->is_exiting()) {
|
||||
results[self_index] = JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
} else {
|
||||
results[self_index] = JVMTI_ERROR_THREAD_SUSPENDED;
|
||||
}
|
||||
} else {
|
||||
results[self_index] = JVMTI_ERROR_NONE; // indicate successful suspend
|
||||
}
|
||||
}
|
||||
// per-thread suspend results returned via results parameter
|
||||
return JVMTI_ERROR_NONE;
|
||||
|
@ -1030,11 +1026,9 @@ JvmtiEnv::ResumeThread(JavaThread* java_thread) {
|
|||
if (java_thread->is_hidden_from_external_view()) {
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (!java_thread->is_being_ext_suspended()) {
|
||||
if (!java_thread->is_suspended()) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!JvmtiSuspendControl::resume(java_thread)) {
|
||||
return JVMTI_ERROR_INTERNAL;
|
||||
}
|
||||
|
@ -1060,7 +1054,7 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt
|
|||
results[i] = JVMTI_ERROR_NONE; // indicate successful resume
|
||||
continue;
|
||||
}
|
||||
if (!java_thread->is_being_ext_suspended()) {
|
||||
if (!java_thread->is_suspended()) {
|
||||
results[i] = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1181,8 +1181,7 @@ MultipleStackTracesCollector::fill_frames(jthread jt, JavaThread *thr, oop threa
|
|||
}
|
||||
|
||||
if (thr != NULL) { // add more state bits if there is a JavaThead to query
|
||||
// same as is_being_ext_suspended() but without locking
|
||||
if (thr->is_ext_suspended() || thr->is_external_suspend()) {
|
||||
if (thr->is_suspended()) {
|
||||
state |= JVMTI_THREAD_STATE_SUSPENDED;
|
||||
}
|
||||
JavaThreadState jts = thr->thread_state();
|
||||
|
@ -1400,7 +1399,7 @@ SetForceEarlyReturn::doit(Thread *target, bool self) {
|
|||
HandleMark hm(current_thread);
|
||||
|
||||
if (!self) {
|
||||
if (!java_thread->is_external_suspend()) {
|
||||
if (!java_thread->is_suspended()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
|
@ -1533,7 +1532,7 @@ UpdateForPopTopFrameClosure::doit(Thread *target, bool self) {
|
|||
JavaThread* java_thread = target->as_Java_thread();
|
||||
assert(java_thread == _state->get_thread(), "Must be");
|
||||
|
||||
if (!self && !java_thread->is_external_suspend()) {
|
||||
if (!self && !java_thread->is_suspended()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
|
@ -1624,7 +1623,7 @@ SetFramePopClosure::doit(Thread *target, bool self) {
|
|||
|
||||
assert(_state->get_thread() == java_thread, "Must be");
|
||||
|
||||
if (!self && !java_thread->is_external_suspend()) {
|
||||
if (!self && !java_thread->is_suspended()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -765,47 +765,13 @@ VM_GetReceiver::VM_GetReceiver(
|
|||
//
|
||||
|
||||
bool JvmtiSuspendControl::suspend(JavaThread *java_thread) {
|
||||
// external suspend should have caught suspending a thread twice
|
||||
|
||||
// Immediate suspension required for JPDA back-end so JVMTI agent threads do
|
||||
// not deadlock due to later suspension on transitions while holding
|
||||
// raw monitors. Passing true causes the immediate suspension.
|
||||
// java_suspend() will catch threads in the process of exiting
|
||||
// and will ignore them.
|
||||
java_thread->java_suspend();
|
||||
|
||||
// It would be nice to have the following assertion in all the time,
|
||||
// but it is possible for a racing resume request to have resumed
|
||||
// this thread right after we suspended it. Temporarily enable this
|
||||
// assertion if you are chasing a different kind of bug.
|
||||
//
|
||||
// assert(java_lang_Thread::thread(java_thread->threadObj()) == NULL ||
|
||||
// java_thread->is_being_ext_suspended(), "thread is not suspended");
|
||||
|
||||
if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) {
|
||||
// check again because we can get delayed in java_suspend():
|
||||
// the thread is in process of exiting.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return java_thread->java_suspend();
|
||||
}
|
||||
|
||||
bool JvmtiSuspendControl::resume(JavaThread *java_thread) {
|
||||
// external suspend should have caught resuming a thread twice
|
||||
assert(java_thread->is_being_ext_suspended(), "thread should be suspended");
|
||||
|
||||
// resume thread
|
||||
{
|
||||
// must always grab Threads_lock, see JVM_SuspendThread
|
||||
MutexLocker ml(Threads_lock);
|
||||
java_thread->java_resume();
|
||||
return java_thread->java_resume();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void JvmtiSuspendControl::print() {
|
||||
#ifndef PRODUCT
|
||||
ResourceMark rm;
|
||||
|
@ -817,7 +783,7 @@ void JvmtiSuspendControl::print() {
|
|||
#else
|
||||
const char *name = "";
|
||||
#endif /*JVMTI_TRACE */
|
||||
log_stream.print("%s(%c ", name, thread->is_being_ext_suspended() ? 'S' : '_');
|
||||
log_stream.print("%s(%c ", name, thread->is_suspended() ? 'S' : '_');
|
||||
if (!thread->has_last_Java_frame()) {
|
||||
log_stream.print("no stack");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
|
@ -241,7 +241,6 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
|
|||
ret = M_INTERRUPTED;
|
||||
} else {
|
||||
ThreadBlockInVM tbivm(jt);
|
||||
jt->set_suspend_equivalent();
|
||||
if (millis <= 0) {
|
||||
self->_ParkEvent->park();
|
||||
} else {
|
||||
|
@ -307,7 +306,8 @@ void JvmtiRawMonitor::simple_notify(Thread* self, bool all) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Any JavaThread will enter here with state _thread_blocked
|
||||
// Any JavaThread will enter here with state _thread_blocked unless we
|
||||
// are in single-threaded mode during startup.
|
||||
void JvmtiRawMonitor::raw_enter(Thread* self) {
|
||||
void* contended;
|
||||
JavaThread* jt = NULL;
|
||||
|
@ -315,15 +315,28 @@ void JvmtiRawMonitor::raw_enter(Thread* self) {
|
|||
// surprise the suspender if a "suspended" thread can still enter monitor
|
||||
if (self->is_Java_thread()) {
|
||||
jt = self->as_Java_thread();
|
||||
jt->SR_lock()->lock_without_safepoint_check();
|
||||
while (jt->is_external_suspend()) {
|
||||
jt->SR_lock()->unlock();
|
||||
jt->java_suspend_self();
|
||||
jt->SR_lock()->lock_without_safepoint_check();
|
||||
}
|
||||
// guarded by SR_lock to avoid racing with new external suspend requests.
|
||||
while (true) {
|
||||
// To pause suspend requests while in blocked we must block handshakes.
|
||||
jt->handshake_state()->lock();
|
||||
// Suspend request flag can only be set in handshakes.
|
||||
// By blocking handshakes, suspend request flag cannot change its value.
|
||||
if (!jt->handshake_state()->is_suspended()) {
|
||||
contended = Atomic::cmpxchg(&_owner, (Thread*)NULL, jt);
|
||||
jt->SR_lock()->unlock();
|
||||
jt->handshake_state()->unlock();
|
||||
break;
|
||||
}
|
||||
jt->handshake_state()->unlock();
|
||||
|
||||
// We may only be in states other than _thread_blocked when we are
|
||||
// in single-threaded mode during startup.
|
||||
guarantee(jt->thread_state() == _thread_blocked, "invariant");
|
||||
|
||||
jt->set_thread_state_fence(_thread_blocked_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_blocked_trans.
|
||||
jt->set_thread_state(_thread_blocked);
|
||||
}
|
||||
} else {
|
||||
contended = Atomic::cmpxchg(&_owner, (Thread*)NULL, self);
|
||||
}
|
||||
|
@ -344,28 +357,24 @@ void JvmtiRawMonitor::raw_enter(Thread* self) {
|
|||
if (!self->is_Java_thread()) {
|
||||
simple_enter(self);
|
||||
} else {
|
||||
// In multi-threaded mode, we must enter this method blocked.
|
||||
guarantee(jt->thread_state() == _thread_blocked, "invariant");
|
||||
for (;;) {
|
||||
jt->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self()
|
||||
simple_enter(jt);
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
if (!jt->handle_special_suspend_equivalent_condition()) {
|
||||
if (!SafepointMechanism::should_process(jt)) {
|
||||
// Not suspended so we're done here.
|
||||
break;
|
||||
}
|
||||
if (!jt->is_suspended()) {
|
||||
// Not suspended so we're done here.
|
||||
break;
|
||||
}
|
||||
|
||||
// This thread was externally suspended
|
||||
// We have reentered the contended monitor, but while we were
|
||||
// waiting another thread suspended us. We don't want to reenter
|
||||
// the monitor while suspended because that would surprise the
|
||||
// thread that suspended us.
|
||||
//
|
||||
// Drop the lock
|
||||
simple_exit(jt);
|
||||
|
||||
jt->java_suspend_self();
|
||||
jt->set_thread_state_fence(_thread_blocked_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_blocked_trans.
|
||||
jt->set_thread_state(_thread_blocked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,30 +420,23 @@ int JvmtiRawMonitor::raw_wait(jlong millis, Thread* self) {
|
|||
|
||||
if (self->is_Java_thread()) {
|
||||
JavaThread* jt = self->as_Java_thread();
|
||||
guarantee(jt->thread_state() == _thread_in_native, "invariant");
|
||||
for (;;) {
|
||||
jt->set_suspend_equivalent();
|
||||
if (!jt->handle_special_suspend_equivalent_condition()) {
|
||||
if (!SafepointMechanism::should_process(jt)) {
|
||||
// Not suspended so we're done here:
|
||||
break;
|
||||
} else {
|
||||
// We've been suspended whilst waiting and so we have to
|
||||
// relinquish the raw monitor until we are resumed. Of course
|
||||
// after reacquiring we have to re-check for suspension again.
|
||||
// Suspension requires we are _thread_blocked, and we also have to
|
||||
// recheck for being interrupted.
|
||||
simple_exit(jt);
|
||||
{
|
||||
ThreadInVMfromNative tivm(jt);
|
||||
{
|
||||
ThreadBlockInVM tbivm(jt);
|
||||
jt->java_suspend_self();
|
||||
}
|
||||
simple_exit(jt);
|
||||
jt->set_thread_state_fence(_thread_in_native_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
if (jt->is_interrupted(true)) {
|
||||
ret = M_INTERRUPTED;
|
||||
}
|
||||
}
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_in_native_trans.
|
||||
jt->set_thread_state(_thread_in_native);
|
||||
simple_enter(jt);
|
||||
}
|
||||
}
|
||||
guarantee(jt == _owner, "invariant");
|
||||
} else {
|
||||
assert(ret != M_INTERRUPTED, "Only JavaThreads can be interrupted");
|
||||
|
|
|
@ -294,11 +294,6 @@ int JvmtiThreadState::cur_stack_depth() {
|
|||
return _cur_stack_depth;
|
||||
}
|
||||
|
||||
bool JvmtiThreadState::may_be_walked() {
|
||||
return (get_thread()->is_being_ext_suspended() || (JavaThread::current() == get_thread()));
|
||||
}
|
||||
|
||||
|
||||
void JvmtiThreadState::process_pending_step_for_popframe() {
|
||||
// We are single stepping as the last part of the PopFrame() dance
|
||||
// so we have some house keeping to do.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
|
@ -312,8 +312,6 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
|
|||
|
||||
public:
|
||||
|
||||
bool may_be_walked();
|
||||
|
||||
// Thread local event collector setter and getter methods.
|
||||
JvmtiDynamicCodeEventCollector* get_dynamic_code_event_collector() {
|
||||
return _dynamic_code_event_collector;
|
||||
|
|
|
@ -521,6 +521,8 @@ static SpecialFlag const special_jvm_flags[] = {
|
|||
{ "InitialRAMFraction", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
{ "AllowRedefinitionToAddDeleteMethods", JDK_Version::jdk(13), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
{ "FlightRecorder", JDK_Version::jdk(13), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
{ "SuspendRetryCount", JDK_Version::undefined(), JDK_Version::jdk(17), JDK_Version::jdk(18) },
|
||||
{ "SuspendRetryDelay", JDK_Version::undefined(), JDK_Version::jdk(17), JDK_Version::jdk(18) },
|
||||
{ "CriticalJNINatives", JDK_Version::jdk(16), JDK_Version::jdk(17), JDK_Version::jdk(18) },
|
||||
{ "AlwaysLockClassLoader", JDK_Version::jdk(17), JDK_Version::jdk(18), JDK_Version::jdk(19) },
|
||||
{ "UseBiasedLocking", JDK_Version::jdk(15), JDK_Version::jdk(18), JDK_Version::jdk(19) },
|
||||
|
|
|
@ -426,16 +426,6 @@ const intx ObjectAlignmentInBytes = 8;
|
|||
"Delay in milliseconds for option AbortVMOnVMOperationTimeout") \
|
||||
range(0, max_intx) \
|
||||
\
|
||||
/* 50 retries * (5 * current_retry_count) millis = ~6.375 seconds */ \
|
||||
/* typically, at most a few retries are needed */ \
|
||||
product(intx, SuspendRetryCount, 50, \
|
||||
"Maximum retry count for an external suspend request") \
|
||||
range(0, max_intx) \
|
||||
\
|
||||
product(intx, SuspendRetryDelay, 5, \
|
||||
"Milliseconds to delay per retry (* current_retry_count)") \
|
||||
range(0, max_intx) \
|
||||
\
|
||||
product(bool, MaxFDLimit, true, \
|
||||
"Bump the number of file descriptors to maximum (Unix only)") \
|
||||
\
|
||||
|
|
|
@ -405,14 +405,14 @@ HandshakeState::HandshakeState(JavaThread* target) :
|
|||
_handshakee(target),
|
||||
_queue(),
|
||||
_lock(Monitor::leaf, "HandshakeState", Mutex::_allow_vm_block_flag, Monitor::_safepoint_check_never),
|
||||
_active_handshaker()
|
||||
_active_handshaker(),
|
||||
_suspended(false),
|
||||
_async_suspend_handshake(false)
|
||||
{
|
||||
}
|
||||
|
||||
void HandshakeState::add_operation(HandshakeOperation* op) {
|
||||
// Adds are done lock free and so is arming.
|
||||
// Calling this method with lock held is considered an error.
|
||||
assert(!_lock.owned_by_self(), "Lock should not be held");
|
||||
_queue.push(op);
|
||||
SafepointMechanism::arm_local_poll_release(_handshakee);
|
||||
}
|
||||
|
@ -453,22 +453,23 @@ HandshakeOperation* HandshakeState::pop() {
|
|||
return _queue.pop(non_self_queue_filter);
|
||||
};
|
||||
|
||||
void HandshakeState::process_by_self() {
|
||||
bool HandshakeState::process_by_self() {
|
||||
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
||||
assert(!_handshakee->is_terminated(), "should not be a terminated thread");
|
||||
assert(_handshakee->thread_state() != _thread_blocked, "should not be in a blocked state");
|
||||
assert(_handshakee->thread_state() != _thread_in_native, "should not be in native");
|
||||
ThreadInVMForHandshake tivm(_handshakee);
|
||||
{
|
||||
// Handshakes cannot safely safepoint.
|
||||
// The exception to this rule is the asynchronous suspension handshake.
|
||||
// It by-passes the NSV by manually doing the transition.
|
||||
NoSafepointVerifier nsv;
|
||||
process_self_inner();
|
||||
return process_self_inner();
|
||||
}
|
||||
}
|
||||
|
||||
void HandshakeState::process_self_inner() {
|
||||
bool HandshakeState::process_self_inner() {
|
||||
while (should_process()) {
|
||||
HandleMark hm(_handshakee);
|
||||
PreserveExceptionMark pem(_handshakee);
|
||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
||||
HandshakeOperation* op = pop_for_self();
|
||||
if (op != NULL) {
|
||||
|
@ -477,13 +478,24 @@ void HandshakeState::process_self_inner() {
|
|||
log_trace(handshake)("Proc handshake %s " INTPTR_FORMAT " on " INTPTR_FORMAT " by self",
|
||||
async ? "asynchronous" : "synchronous", p2i(op), p2i(_handshakee));
|
||||
op->prepare(_handshakee, _handshakee);
|
||||
if (!async) {
|
||||
HandleMark hm(_handshakee);
|
||||
PreserveExceptionMark pem(_handshakee);
|
||||
op->do_handshake(_handshakee);
|
||||
} else {
|
||||
// An asynchronous handshake may put the JavaThread in blocked state (safepoint safe).
|
||||
// The destructor ~PreserveExceptionMark touches the exception oop so it must not be executed,
|
||||
// since a safepoint may be in-progress when returning from the async handshake.
|
||||
op->do_handshake(_handshakee);
|
||||
if (async) {
|
||||
log_handshake_info(((AsyncHandshakeOperation*)op)->start_time(), op->name(), 1, 0, "asynchronous");
|
||||
delete op;
|
||||
return true; // Must check for safepoints
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandshakeState::can_process_handshake() {
|
||||
|
@ -587,3 +599,102 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma
|
|||
pr_ret == HandshakeState::_succeeded ? "including" : "excluding", p2i(match_op));
|
||||
return pr_ret;
|
||||
}
|
||||
|
||||
void HandshakeState::lock() {
|
||||
_lock.lock_without_safepoint_check();
|
||||
}
|
||||
|
||||
void HandshakeState::unlock() {
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
void HandshakeState::do_self_suspend() {
|
||||
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
||||
assert(_lock.owned_by_self(), "Lock must be held");
|
||||
assert(!_handshakee->has_last_Java_frame() || _handshakee->frame_anchor()->walkable(), "should have walkable stack");
|
||||
JavaThreadState jts = _handshakee->thread_state();
|
||||
while (is_suspended()) {
|
||||
_handshakee->set_thread_state(_thread_blocked);
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_handshakee));
|
||||
_lock.wait_without_safepoint_check();
|
||||
}
|
||||
_handshakee->set_thread_state(jts);
|
||||
set_async_suspend_handshake(false);
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_handshakee));
|
||||
}
|
||||
|
||||
// This is the closure that prevents a suspended JavaThread from
|
||||
// escaping the suspend request.
|
||||
class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
|
||||
public:
|
||||
ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {}
|
||||
void do_thread(Thread* thr) {
|
||||
JavaThread* current = thr->as_Java_thread();
|
||||
assert(current == Thread::current(), "Must be self executed.");
|
||||
current->handshake_state()->do_self_suspend();
|
||||
}
|
||||
};
|
||||
|
||||
bool HandshakeState::suspend_with_handshake() {
|
||||
if (_handshakee->is_exiting() ||
|
||||
_handshakee->threadObj() == NULL) {
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee));
|
||||
return false;
|
||||
}
|
||||
if (has_async_suspend_handshake()) {
|
||||
if (is_suspended()) {
|
||||
// Target is already suspended.
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_handshakee));
|
||||
return false;
|
||||
} else {
|
||||
// Target is going to wake up and leave suspension.
|
||||
// Let's just stop the thread from doing that.
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee));
|
||||
set_suspended(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// no suspend request
|
||||
assert(!is_suspended(), "cannot be suspended without a suspend request");
|
||||
// Thread is safe, so it must execute the request, thus we can count it as suspended
|
||||
// from this point.
|
||||
set_suspended(true);
|
||||
set_async_suspend_handshake(true);
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee));
|
||||
ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake();
|
||||
Handshake::execute(ts, _handshakee);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is the closure that synchronously honors the suspend request.
|
||||
class SuspendThreadHandshake : public HandshakeClosure {
|
||||
bool _did_suspend;
|
||||
public:
|
||||
SuspendThreadHandshake() : HandshakeClosure("SuspendThread"), _did_suspend(false) {}
|
||||
void do_thread(Thread* thr) {
|
||||
JavaThread* target = thr->as_Java_thread();
|
||||
_did_suspend = target->handshake_state()->suspend_with_handshake();
|
||||
}
|
||||
bool did_suspend() { return _did_suspend; }
|
||||
};
|
||||
|
||||
bool HandshakeState::suspend() {
|
||||
SuspendThreadHandshake st;
|
||||
Handshake::execute(&st, _handshakee);
|
||||
return st.did_suspend();
|
||||
}
|
||||
|
||||
bool HandshakeState::resume() {
|
||||
if (!is_suspended()) {
|
||||
return false;
|
||||
}
|
||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (!is_suspended()) {
|
||||
assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request");
|
||||
return false;
|
||||
}
|
||||
// Resume the thread.
|
||||
set_suspended(false);
|
||||
_lock.notify();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2021, 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
|
||||
|
@ -33,6 +33,8 @@
|
|||
|
||||
class HandshakeOperation;
|
||||
class JavaThread;
|
||||
class SuspendThreadHandshake;
|
||||
class ThreadSelfSuspensionHandshake;
|
||||
|
||||
// A handshake closure is a callback that is executed for a JavaThread
|
||||
// while it is in a safepoint/handshake-safe state. Depending on the
|
||||
|
@ -64,30 +66,46 @@ class Handshake : public AllStatic {
|
|||
static void execute(AsyncHandshakeClosure* hs_cl, JavaThread* target);
|
||||
};
|
||||
|
||||
class JvmtiRawMonitor;
|
||||
|
||||
// The HandshakeState keeps track of an ongoing handshake for this JavaThread.
|
||||
// VMThread/Handshaker and JavaThread are serialized with _lock making sure the
|
||||
// operation is only done by either VMThread/Handshaker on behalf of the
|
||||
// JavaThread or by the target JavaThread itself.
|
||||
class HandshakeState {
|
||||
friend JvmtiRawMonitor;
|
||||
friend ThreadSelfSuspensionHandshake;
|
||||
friend SuspendThreadHandshake;
|
||||
friend JavaThread;
|
||||
// This a back reference to the JavaThread,
|
||||
// the target for all operation in the queue.
|
||||
JavaThread* _handshakee;
|
||||
// The queue containing handshake operations to be performed on _handshakee.
|
||||
FilterQueue<HandshakeOperation*> _queue;
|
||||
// Provides mutual exclusion to this state and queue.
|
||||
Mutex _lock;
|
||||
// Provides mutual exclusion to this state and queue. Also used for
|
||||
// JavaThread suspend/resume operations.
|
||||
Monitor _lock;
|
||||
// Set to the thread executing the handshake operation.
|
||||
Thread* _active_handshaker;
|
||||
|
||||
bool claim_handshake();
|
||||
bool possibly_can_process_handshake();
|
||||
bool can_process_handshake();
|
||||
void process_self_inner();
|
||||
|
||||
// Returns false if the JavaThread finished all its handshake operations.
|
||||
// If the method returns true there is still potential work to be done,
|
||||
// but we need to check for a safepoint before.
|
||||
// (This is due to a suspension handshake which put the JavaThread in blocked
|
||||
// state so a safepoint may be in-progress.)
|
||||
bool process_self_inner();
|
||||
|
||||
bool have_non_self_executable_operation();
|
||||
HandshakeOperation* pop_for_self();
|
||||
HandshakeOperation* pop();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
public:
|
||||
HandshakeState(JavaThread* thread);
|
||||
|
||||
|
@ -107,10 +125,21 @@ class HandshakeState {
|
|||
// while handshake operations are being executed, the _handshakee
|
||||
// must take slow path, process_by_self(), if _lock is held.
|
||||
bool should_process() {
|
||||
return !_queue.is_empty() || _lock.is_locked();
|
||||
// The holder of the _lock can add an asynchronous handshake to queue.
|
||||
// To make sure it is seen by the handshakee, the handshakee must first
|
||||
// check the _lock, and if held go to slow path.
|
||||
// Since the handshakee is unsafe if _lock gets locked after this check
|
||||
// we know other threads cannot process any handshakes.
|
||||
// Now we can check the queue to see if there is anything we should processs.
|
||||
if (_lock.is_locked()) {
|
||||
return true;
|
||||
}
|
||||
// Lock check must be done before queue check, force ordering.
|
||||
OrderAccess::loadload();
|
||||
return !_queue.is_empty();
|
||||
}
|
||||
|
||||
void process_by_self();
|
||||
bool process_by_self();
|
||||
|
||||
enum ProcessResult {
|
||||
_no_operation = 0,
|
||||
|
@ -123,6 +152,31 @@ class HandshakeState {
|
|||
ProcessResult try_process(HandshakeOperation* match_op);
|
||||
|
||||
Thread* active_handshaker() const { return _active_handshaker; }
|
||||
|
||||
// Suspend/resume support
|
||||
private:
|
||||
// This flag is true when the thread owning this
|
||||
// HandshakeState (the _handshakee) is suspended.
|
||||
volatile bool _suspended;
|
||||
// This flag is true while there is async handshake (trap)
|
||||
// on queue. Since we do only need one, we can reuse it if
|
||||
// thread gets suspended again (after a resume)
|
||||
// and we have not yet processed it.
|
||||
bool _async_suspend_handshake;
|
||||
|
||||
// Called from the suspend handshake.
|
||||
bool suspend_with_handshake();
|
||||
// Called from the async handshake (the trap)
|
||||
// to stop a thread from continuing execution when suspended.
|
||||
void do_self_suspend();
|
||||
|
||||
bool is_suspended() { return Atomic::load(&_suspended); }
|
||||
void set_suspended(bool to) { return Atomic::store(&_suspended, to); }
|
||||
bool has_async_suspend_handshake() { return _async_suspend_handshake; }
|
||||
void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; }
|
||||
|
||||
bool suspend();
|
||||
bool resume();
|
||||
};
|
||||
|
||||
#endif // SHARE_RUNTIME_HANDSHAKE_HPP
|
||||
|
|
|
@ -81,10 +81,6 @@ void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAP
|
|||
// will be handled by safepoint correctly when this thread is
|
||||
// notified at a safepoint.
|
||||
|
||||
// This ThreadBlockInVM object is not also considered to be
|
||||
// suspend-equivalent because MonitorDeflationThread is not
|
||||
// visible to external suspension.
|
||||
|
||||
ThreadBlockInVM tbivm(jt);
|
||||
|
||||
MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
|
|
@ -216,7 +216,7 @@ bool Monitor::wait_without_safepoint_check(int64_t timeout) {
|
|||
return wait_status != 0; // return true IFF timeout
|
||||
}
|
||||
|
||||
bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
|
||||
bool Monitor::wait(int64_t timeout) {
|
||||
JavaThread* const self = JavaThread::current();
|
||||
// Safepoint checking logically implies an active JavaThread.
|
||||
assert(self->is_active_Java_thread(), "invariant");
|
||||
|
@ -239,25 +239,9 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
|
|||
{
|
||||
ThreadBlockInVM tbivmdc(self, &in_flight_mutex);
|
||||
OSThreadWaitState osts(self->osthread(), false /* not Object.wait() */);
|
||||
if (as_suspend_equivalent) {
|
||||
self->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self()
|
||||
}
|
||||
|
||||
wait_status = _lock.wait(timeout);
|
||||
in_flight_mutex = this; // save for ~ThreadBlockInVM
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
if (as_suspend_equivalent && self->handle_special_suspend_equivalent_condition()) {
|
||||
// Our event wait has finished and we own the lock, but
|
||||
// while we were waiting another thread suspended us. We don't
|
||||
// want to hold the lock while suspended because that
|
||||
// would surprise the thread that suspended us.
|
||||
_lock.unlock();
|
||||
self->java_suspend_self();
|
||||
_lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
if (in_flight_mutex != NULL) {
|
||||
|
@ -276,9 +260,9 @@ Mutex::~Mutex() {
|
|||
assert_owner(NULL);
|
||||
}
|
||||
|
||||
// Only Threads_lock, Heap_lock and SR_lock may be safepoint_check_sometimes.
|
||||
// Only Threads_lock and Heap_lock may be safepoint_check_sometimes.
|
||||
bool is_sometimes_ok(const char* name) {
|
||||
return (strcmp(name, "Threads_lock") == 0 || strcmp(name, "Heap_lock") == 0 || strcmp(name, "SR_lock") == 0);
|
||||
return (strcmp(name, "Threads_lock") == 0 || strcmp(name, "Heap_lock") == 0);
|
||||
}
|
||||
|
||||
Mutex::Mutex(int Rank, const char * name, bool allow_vm_block,
|
||||
|
@ -388,10 +372,9 @@ void Mutex::check_rank(Thread* thread) {
|
|||
}
|
||||
}
|
||||
|
||||
// Locks with rank native or suspend_resume are an exception and are not
|
||||
// Locks with rank native are an exception and are not
|
||||
// subject to the verification rules.
|
||||
bool check_can_be_skipped = this->rank() == Mutex::native || this->rank() == Mutex::suspend_resume
|
||||
|| SafepointSynchronize::is_at_safepoint();
|
||||
bool check_can_be_skipped = this->rank() == Mutex::native || SafepointSynchronize::is_at_safepoint();
|
||||
if (owned_by_self()) {
|
||||
// wait() case
|
||||
Mutex* least = get_least_ranked_lock_besides_this(locks_owned);
|
||||
|
|
|
@ -67,8 +67,7 @@ class Mutex : public CHeapObj<mtSynchronizer> {
|
|||
access = event + 1,
|
||||
tty = access + 2,
|
||||
special = tty + 3,
|
||||
suspend_resume = special + 1,
|
||||
oopstorage = suspend_resume + 2,
|
||||
oopstorage = special + 3,
|
||||
leaf = oopstorage + 2,
|
||||
safepoint = leaf + 10,
|
||||
barrier = safepoint + 1,
|
||||
|
@ -123,7 +122,6 @@ class Mutex : public CHeapObj<mtSynchronizer> {
|
|||
|
||||
public:
|
||||
static const bool _allow_vm_block_flag = true;
|
||||
static const bool _as_suspend_equivalent_flag = true;
|
||||
|
||||
// Locks can be acquired with or without a safepoint check. NonJavaThreads do not follow
|
||||
// the safepoint protocol when acquiring locks.
|
||||
|
@ -222,10 +220,8 @@ class Monitor : public Mutex {
|
|||
|
||||
// Wait until monitor is notified (or times out).
|
||||
// Defaults are to make safepoint checks, wait time is forever (i.e.,
|
||||
// zero), and not a suspend-equivalent condition. Returns true if wait
|
||||
// times out; otherwise returns false.
|
||||
bool wait(int64_t timeout = 0,
|
||||
bool as_suspend_equivalent = !_as_suspend_equivalent_flag);
|
||||
// zero). Returns true if wait times out; otherwise returns false.
|
||||
bool wait(int64_t timeout = 0);
|
||||
bool wait_without_safepoint_check(int64_t timeout = 0);
|
||||
void notify();
|
||||
void notify_all();
|
||||
|
|
|
@ -259,10 +259,9 @@ class MonitorLocker: public MutexLocker {
|
|||
assert(monitor != NULL, "NULL monitor not allowed");
|
||||
}
|
||||
|
||||
bool wait(int64_t timeout = 0,
|
||||
bool as_suspend_equivalent = !Mutex::_as_suspend_equivalent_flag) {
|
||||
bool wait(int64_t timeout = 0) {
|
||||
if (_flag == Mutex::_safepoint_check_flag) {
|
||||
return as_monitor()->wait(timeout, as_suspend_equivalent);
|
||||
return as_monitor()->wait(timeout);
|
||||
} else {
|
||||
return as_monitor()->wait_without_safepoint_check(timeout);
|
||||
}
|
||||
|
|
|
@ -323,9 +323,8 @@ void WatcherThread::stop() {
|
|||
MonitorLocker mu(Terminator_lock);
|
||||
|
||||
while (watcher_thread() != NULL) {
|
||||
// This wait should make safepoint checks, wait without a timeout,
|
||||
// and wait as a suspend-equivalent condition.
|
||||
mu.wait(0, Mutex::_as_suspend_equivalent_flag);
|
||||
// This wait should make safepoint checks and wait without a timeout.
|
||||
mu.wait(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ void NotificationThread::notification_thread_entry(JavaThread* jt, TRAPS) {
|
|||
(has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) |
|
||||
(has_gc_notification_event = GCNotifier::has_event()))
|
||||
== 0) {
|
||||
// Wait as a suspend equalent until notified that there is some work to do.
|
||||
ml.wait(0, true);
|
||||
// Wait until notified that there is some work to do.
|
||||
ml.wait(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -403,39 +403,49 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
|||
}
|
||||
|
||||
OSThreadContendState osts(current->osthread());
|
||||
ThreadBlockInVM tbivm(current);
|
||||
|
||||
// TODO-FIXME: change the following for(;;) loop to straight-line code.
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
for (;;) {
|
||||
current->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition()
|
||||
// or java_suspend_self()
|
||||
|
||||
current->set_thread_state(_thread_blocked);
|
||||
EnterI(current);
|
||||
|
||||
if (!current->handle_special_suspend_equivalent_condition()) break;
|
||||
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current) &&
|
||||
current->is_suspended()) {
|
||||
// We have acquired the contended monitor, but while we were
|
||||
// waiting another thread suspended us. We don't want to enter
|
||||
// the monitor while suspended because that would surprise the
|
||||
// thread that suspended us.
|
||||
//
|
||||
_recursions = 0;
|
||||
_succ = NULL;
|
||||
// Don't need a full fence after clearing successor here because of the call to exit().
|
||||
exit(current, false /* not_suspended */);
|
||||
|
||||
current->java_suspend_self();
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
// Since we are going to _thread_blocked we skip setting _thread_in_vm here.
|
||||
} else {
|
||||
// Only exit path from for loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current->set_current_pending_monitor(NULL);
|
||||
|
||||
// We cleared the pending monitor info since we've just gotten past
|
||||
// the enter-check-for-suspend dance and we now own the monitor free
|
||||
// and clear, i.e., it is no longer pending. The ThreadBlockInVM
|
||||
// destructor can go to a safepoint at the end of this block. If we
|
||||
// and clear, i.e., it is no longer pending.
|
||||
// We can go to a safepoint at the end of this block. If we
|
||||
// do a thread dump during that safepoint, then this thread will show
|
||||
// as having "-locked" the monitor, but the OS and java.lang.Thread
|
||||
// states will still report that the thread is blocked trying to
|
||||
// acquire it.
|
||||
|
||||
// Completed the tranisition.
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
add_to_contentions(-1);
|
||||
|
@ -954,25 +964,26 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
|
|||
if (TryLock(current) > 0) break;
|
||||
if (TrySpin(current) > 0) break;
|
||||
|
||||
// State transition wrappers around park() ...
|
||||
// ReenterI() wisely defers state transitions until
|
||||
// it's clear we must park the thread.
|
||||
{
|
||||
OSThreadContendState osts(current->osthread());
|
||||
ThreadBlockInVM tbivm(current);
|
||||
|
||||
// cleared by handle_special_suspend_equivalent_condition()
|
||||
// or java_suspend_self()
|
||||
current->set_suspend_equivalent();
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
current->set_thread_state(_thread_blocked);
|
||||
current->_ParkEvent->park();
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
for (;;) {
|
||||
if (!current->handle_special_suspend_equivalent_condition()) break;
|
||||
if (_succ == current) { _succ = NULL; OrderAccess::fence(); }
|
||||
current->java_suspend_self();
|
||||
current->set_suspend_equivalent();
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current)) {
|
||||
if (_succ == current) {
|
||||
_succ = NULL;
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
}
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
// Try again, but just so we distinguish between futile wakeups and
|
||||
|
@ -1526,11 +1537,15 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
|||
{ // State transition wrappers
|
||||
OSThread* osthread = current->osthread();
|
||||
OSThreadWaitState osts(osthread, true);
|
||||
{
|
||||
ThreadBlockInVM tbivm(current);
|
||||
// Thread is in thread_blocked state and oop access is unsafe.
|
||||
current->set_suspend_equivalent();
|
||||
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
{
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
current->set_thread_state(_thread_blocked);
|
||||
if (interrupted || HAS_PENDING_EXCEPTION) {
|
||||
// Intentionally empty
|
||||
} else if (node._notified == 0) {
|
||||
|
@ -1540,14 +1555,16 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
|||
ret = current->_ParkEvent->park(millis);
|
||||
}
|
||||
}
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
if (current->handle_special_suspend_equivalent_condition()) {
|
||||
// TODO-FIXME: add -- if succ == current then succ = null.
|
||||
current->java_suspend_self();
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current)) {
|
||||
if (_succ == current) {
|
||||
_succ = NULL;
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
}
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
} // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm
|
||||
|
||||
// Node may be on the WaitSet, the EntryList (or cxq), or in transition
|
||||
// from the WaitSet to the EntryList.
|
||||
|
|
|
@ -871,8 +871,6 @@ int os::random() {
|
|||
// locking.
|
||||
|
||||
void os::start_thread(Thread* thread) {
|
||||
// guard suspend/resume
|
||||
MutexLocker ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
OSThread* osthread = thread->osthread();
|
||||
osthread->set_state(RUNNABLE);
|
||||
pd_start_thread(thread);
|
||||
|
|
|
@ -77,8 +77,10 @@ void SafepointMechanism::default_initialize() {
|
|||
}
|
||||
|
||||
void SafepointMechanism::process(JavaThread *thread) {
|
||||
bool need_rechecking;
|
||||
do {
|
||||
if (global_poll()) {
|
||||
// Any load in ::block must not pass the global poll load.
|
||||
// Any load in ::block() must not pass the global poll load.
|
||||
// Otherwise we might load an old safepoint counter (for example).
|
||||
OrderAccess::loadload();
|
||||
SafepointSynchronize::block(thread);
|
||||
|
@ -86,15 +88,15 @@ void SafepointMechanism::process(JavaThread *thread) {
|
|||
|
||||
// The call to on_safepoint fixes the thread's oops and the first few frames.
|
||||
//
|
||||
// The call has been carefully placed here to cater for a few situations:
|
||||
// The call has been carefully placed here to cater to a few situations:
|
||||
// 1) After we exit from block after a global poll
|
||||
// 2) After a thread races with the disarming of the global poll and transitions from native/blocked
|
||||
// 3) Before the handshake code is run
|
||||
StackWatermarkSet::on_safepoint(thread);
|
||||
|
||||
if (thread->handshake_state()->should_process()) {
|
||||
thread->handshake_state()->process_by_self();
|
||||
}
|
||||
need_rechecking = thread->handshake_state()->should_process() && thread->handshake_state()->process_by_self();
|
||||
|
||||
} while (need_rechecking);
|
||||
}
|
||||
|
||||
uintptr_t SafepointMechanism::compute_poll_word(bool armed, uintptr_t stack_watermark) {
|
||||
|
@ -111,6 +113,8 @@ uintptr_t SafepointMechanism::compute_poll_word(bool armed, uintptr_t stack_wate
|
|||
}
|
||||
|
||||
void SafepointMechanism::update_poll_values(JavaThread* thread) {
|
||||
assert(thread->thread_state() != _thread_blocked, "Must not be");
|
||||
assert(thread->thread_state() != _thread_in_native, "Must not be");
|
||||
for (;;) {
|
||||
bool armed = global_poll() || thread->handshake_state()->has_operation();
|
||||
uintptr_t stack_watermark = StackWatermarkSet::lowest_watermark(thread);
|
||||
|
|
|
@ -273,7 +273,6 @@ void NMethodSweeper::handle_safepoint_request() {
|
|||
MutexUnlocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
ThreadBlockInVM tbivm(thread);
|
||||
thread->java_suspend_self();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -274,8 +274,6 @@ Thread::Thread() {
|
|||
_visited_for_critical_count = false;
|
||||
#endif
|
||||
|
||||
_SR_lock = new Monitor(Mutex::suspend_resume, "SR_lock", true,
|
||||
Monitor::_safepoint_check_sometimes);
|
||||
_suspend_flags = 0;
|
||||
|
||||
// thread-specific hashCode stream generator state - Marsaglia shift-xor form
|
||||
|
@ -450,18 +448,13 @@ Thread::~Thread() {
|
|||
delete last_handle_mark();
|
||||
assert(last_handle_mark() == NULL, "check we have reached the end");
|
||||
|
||||
// It's possible we can encounter a null _ParkEvent, etc., in stillborn threads.
|
||||
// We NULL out the fields for good hygiene.
|
||||
ParkEvent::Release(_ParkEvent); _ParkEvent = NULL;
|
||||
ParkEvent::Release(_ParkEvent);
|
||||
// Set to NULL as a termination indicator for has_terminated().
|
||||
Atomic::store(&_ParkEvent, (ParkEvent*)NULL);
|
||||
|
||||
delete handle_area();
|
||||
delete metadata_handles();
|
||||
|
||||
// SR_handler uses this as a termination indicator -
|
||||
// needs to happen before os::free_thread()
|
||||
delete _SR_lock;
|
||||
_SR_lock = NULL;
|
||||
|
||||
// osthread() can be NULL, if creation of thread failed.
|
||||
if (osthread() != NULL) os::free_thread(osthread());
|
||||
|
||||
|
@ -582,119 +575,6 @@ void Thread::send_async_exception(oop java_thread, oop java_throwable) {
|
|||
Handshake::execute(&vm_stop, target);
|
||||
}
|
||||
|
||||
|
||||
// Check if an external suspend request has completed (or has been
|
||||
// cancelled). Returns true if the thread is externally suspended and
|
||||
// false otherwise.
|
||||
bool JavaThread::is_ext_suspend_completed() {
|
||||
bool did_trans_retry = false; // only do thread_in_native_trans retry once
|
||||
bool do_trans_retry; // flag to force the retry
|
||||
|
||||
do {
|
||||
do_trans_retry = false;
|
||||
|
||||
if (is_exiting()) {
|
||||
// Thread is in the process of exiting. This is always checked
|
||||
// first to reduce the risk of dereferencing a freed JavaThread.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_external_suspend()) {
|
||||
// Suspend request is cancelled. This is always checked before
|
||||
// is_ext_suspended() to reduce the risk of a rogue resume
|
||||
// confusing the thread that made the suspend request.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_ext_suspended()) {
|
||||
// thread is suspended
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now that we no longer do hard suspends of threads running
|
||||
// native code, the target thread can be changing thread state
|
||||
// while we are in this routine:
|
||||
//
|
||||
// _thread_in_native -> _thread_in_native_trans -> _thread_blocked
|
||||
//
|
||||
// We save a copy of the thread state as observed at this moment
|
||||
// and make our decision about suspend completeness based on the
|
||||
// copy. This closes the race where the thread state is seen as
|
||||
// _thread_in_native_trans in the if-thread_blocked check, but is
|
||||
// seen as _thread_blocked in if-thread_in_native_trans check.
|
||||
JavaThreadState save_state = thread_state();
|
||||
|
||||
if (save_state == _thread_blocked && is_suspend_equivalent()) {
|
||||
// If the thread's state is _thread_blocked and this blocking
|
||||
// condition is known to be equivalent to a suspend, then we can
|
||||
// consider the thread to be externally suspended. This means that
|
||||
// the code that sets _thread_blocked has been modified to do
|
||||
// self-suspension if the blocking condition releases. We also
|
||||
// used to check for CONDVAR_WAIT here, but that is now covered by
|
||||
// the _thread_blocked with self-suspension check.
|
||||
//
|
||||
// Return true since we wouldn't be here unless there was still an
|
||||
// external suspend request.
|
||||
return true;
|
||||
} else if (save_state == _thread_in_native && frame_anchor()->walkable()) {
|
||||
// Threads running native code will self-suspend on native==>VM/Java
|
||||
// transitions. If its stack is walkable (should always be the case
|
||||
// unless this function is called before the actual java_suspend()
|
||||
// call), then the wait is done.
|
||||
return true;
|
||||
} else if (!did_trans_retry &&
|
||||
save_state == _thread_in_native_trans &&
|
||||
frame_anchor()->walkable()) {
|
||||
// The thread is transitioning from thread_in_native to another
|
||||
// thread state. check_safepoint_and_suspend_for_native_trans()
|
||||
// will force the thread to self-suspend. If it hasn't gotten
|
||||
// there yet we may have caught the thread in-between the native
|
||||
// code check above and the self-suspend.
|
||||
//
|
||||
// Since we use the saved thread state in the if-statement above,
|
||||
// there is a chance that the thread has already transitioned to
|
||||
// _thread_blocked by the time we get here. In that case, we will
|
||||
// make a single unnecessary pass through the logic below. This
|
||||
// doesn't hurt anything since we still do the trans retry.
|
||||
|
||||
// Once the thread leaves thread_in_native_trans for another
|
||||
// thread state, we break out of this retry loop. We shouldn't
|
||||
// need this flag to prevent us from getting back here, but
|
||||
// sometimes paranoia is good.
|
||||
did_trans_retry = true;
|
||||
|
||||
// We wait for the thread to transition to a more usable state.
|
||||
for (int i = 1; i <= SuspendRetryCount; i++) {
|
||||
// We used to do an "os::yield_all(i)" call here with the intention
|
||||
// that yielding would increase on each retry. However, the parameter
|
||||
// is ignored on Linux which means the yield didn't scale up. Waiting
|
||||
// on the SR_lock below provides a much more predictable scale up for
|
||||
// the delay. It also provides a simple/direct point to check for any
|
||||
// safepoint requests from the VMThread
|
||||
|
||||
// temporarily drops SR_lock while doing wait with safepoint check
|
||||
// (if we're a JavaThread - the WatcherThread can also call this)
|
||||
// and increase delay with each retry
|
||||
if (Thread::current()->is_Java_thread()) {
|
||||
SR_lock()->wait(i * SuspendRetryDelay);
|
||||
} else {
|
||||
SR_lock()->wait_without_safepoint_check(i * SuspendRetryDelay);
|
||||
}
|
||||
|
||||
// check the actual thread state instead of what we saved above
|
||||
if (thread_state() != _thread_in_native_trans) {
|
||||
// the thread has transitioned to another thread state so
|
||||
// try all the checks (except this one) one more time.
|
||||
do_trans_retry = true;
|
||||
break;
|
||||
}
|
||||
} // end retry loop
|
||||
}
|
||||
} while (do_trans_retry);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// GC Support
|
||||
bool Thread::claim_par_threads_do(uintx claim_token) {
|
||||
uintx token = _threads_do_token;
|
||||
|
@ -1189,7 +1069,6 @@ JavaThread::JavaThread() :
|
|||
_saved_exception_pc(nullptr),
|
||||
|
||||
_terminated(_not_terminated),
|
||||
_suspend_equivalent(false),
|
||||
_in_deopt_handler(0),
|
||||
_doing_unsafe_access(false),
|
||||
_do_not_unlock_if_synchronized(false),
|
||||
|
@ -1237,7 +1116,6 @@ JavaThread::JavaThread() :
|
|||
|
||||
_SleepEvent(ParkEvent::Allocate(this))
|
||||
{
|
||||
|
||||
set_jni_functions(jni_functions());
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
|
@ -1563,33 +1441,12 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
|
|||
JvmtiExport::post_thread_end(this);
|
||||
}
|
||||
|
||||
// We have notified the agents that we are exiting, before we go on,
|
||||
// we must check for a pending external suspend request and honor it
|
||||
// in order to not surprise the thread that made the suspend request.
|
||||
while (true) {
|
||||
{
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (!is_external_suspend()) {
|
||||
// The careful dance between thread suspension and exit is handled here.
|
||||
// Since we are in thread_in_vm state and suspension is done with handshakes,
|
||||
// we can just put in the exiting state and it will be correctly handled.
|
||||
set_terminated(_thread_exiting);
|
||||
|
||||
ThreadService::current_thread_exiting(this, is_daemon(threadObj()));
|
||||
break;
|
||||
}
|
||||
// Implied else:
|
||||
// Things get a little tricky here. We have a pending external
|
||||
// suspend request, but we are holding the SR_lock so we
|
||||
// can't just self-suspend. So we temporarily drop the lock
|
||||
// and then self-suspend.
|
||||
}
|
||||
|
||||
ThreadBlockInVM tbivm(this);
|
||||
java_suspend_self();
|
||||
|
||||
// We're done with this suspend request, but we have to loop around
|
||||
// and check again. Eventually we will get SR_lock without a pending
|
||||
// external suspend request and will be able to mark ourselves as
|
||||
// exiting.
|
||||
}
|
||||
// no more external suspends are allowed at this point
|
||||
} else {
|
||||
assert(!is_terminated() && !is_exiting(), "must not be exiting");
|
||||
// before_exit() has already posted JVMTI THREAD_END events
|
||||
|
@ -1863,12 +1720,6 @@ void JavaThread::check_and_handle_async_exceptions(bool check_unsafe_error) {
|
|||
|
||||
void JavaThread::handle_special_runtime_exit_condition(bool check_asyncs) {
|
||||
|
||||
// Check for pending external suspend.
|
||||
if (is_external_suspend_with_lock()) {
|
||||
frame_anchor()->make_walkable(this);
|
||||
java_suspend_self_with_safepoint_check();
|
||||
}
|
||||
|
||||
if (is_obj_deopt_suspend()) {
|
||||
frame_anchor()->make_walkable(this);
|
||||
wait_for_object_deoptimization();
|
||||
|
@ -1930,164 +1781,42 @@ void JavaThread::send_thread_stop(oop java_throwable) {
|
|||
this->interrupt();
|
||||
}
|
||||
|
||||
|
||||
// External suspension mechanism.
|
||||
//
|
||||
// Tell the VM to suspend a thread when ever it knows that it does not hold on
|
||||
// to any VM_locks and it is at a transition
|
||||
// Self-suspension will happen on the transition out of the vm.
|
||||
// Catch "this" coming in from JNIEnv pointers when the thread has been freed
|
||||
// Guarantees on return (for a valid target thread):
|
||||
// - Target thread will not execute any new bytecode.
|
||||
// - Target thread will not enter any new monitors.
|
||||
//
|
||||
// Guarantees on return:
|
||||
// + Target thread will not execute any new bytecode (that's why we need to
|
||||
// force a safepoint)
|
||||
// + Target thread will not enter any new monitors
|
||||
//
|
||||
void JavaThread::java_suspend() {
|
||||
bool JavaThread::java_suspend() {
|
||||
ThreadsListHandle tlh;
|
||||
if (!tlh.includes(this) || threadObj() == NULL || is_exiting()) {
|
||||
return;
|
||||
if (!tlh.includes(this)) {
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " not on ThreadsList, no suspension", p2i(this));
|
||||
return false;
|
||||
}
|
||||
return this->handshake_state()->suspend();
|
||||
}
|
||||
|
||||
{ MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (!is_external_suspend()) {
|
||||
// a racing resume has cancelled us; bail out now
|
||||
return;
|
||||
bool JavaThread::java_resume() {
|
||||
ThreadsListHandle tlh;
|
||||
if (!tlh.includes(this)) {
|
||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " not on ThreadsList, nothing to resume", p2i(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
// suspend is done
|
||||
|
||||
// Warning: is_ext_suspend_completed() may temporarily drop the
|
||||
// SR_lock to allow the thread to reach a stable thread state if
|
||||
// it is currently in a transient thread state.
|
||||
if (is_ext_suspend_completed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Thread::current() == this) {
|
||||
// Safely self-suspend.
|
||||
// If we don't do this explicitly it will implicitly happen
|
||||
// before we transition back to Java, and on some other thread-state
|
||||
// transition paths, but not as we exit a JVM TI SuspendThread call.
|
||||
// As SuspendThread(current) must not return (until resumed) we must
|
||||
// self-suspend here.
|
||||
ThreadBlockInVM tbivm(this);
|
||||
java_suspend_self();
|
||||
} else {
|
||||
VM_ThreadSuspend vm_suspend;
|
||||
VMThread::execute(&vm_suspend);
|
||||
}
|
||||
}
|
||||
|
||||
// Part II of external suspension.
|
||||
// A JavaThread self suspends when it detects a pending external suspend
|
||||
// request. This is usually on transitions. It is also done in places
|
||||
// where continuing to the next transition would surprise the caller,
|
||||
// e.g., monitor entry.
|
||||
//
|
||||
// Returns the number of times that the thread self-suspended.
|
||||
//
|
||||
// Note: DO NOT call java_suspend_self() when you just want to block current
|
||||
// thread. java_suspend_self() is the second stage of cooperative
|
||||
// suspension for external suspend requests and should only be used
|
||||
// to complete an external suspend request.
|
||||
//
|
||||
int JavaThread::java_suspend_self() {
|
||||
assert(thread_state() == _thread_blocked, "wrong state for java_suspend_self()");
|
||||
int ret = 0;
|
||||
|
||||
// we are in the process of exiting so don't suspend
|
||||
if (is_exiting()) {
|
||||
clear_external_suspend();
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(_anchor.walkable() || !has_last_Java_frame(),
|
||||
"must have walkable stack");
|
||||
|
||||
MonitorLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
|
||||
assert(!this->is_ext_suspended(),
|
||||
"a thread trying to self-suspend should not already be suspended");
|
||||
|
||||
if (this->is_suspend_equivalent()) {
|
||||
// If we are self-suspending as a result of the lifting of a
|
||||
// suspend equivalent condition, then the suspend_equivalent
|
||||
// flag is not cleared until we set the ext_suspended flag.
|
||||
this->clear_suspend_equivalent();
|
||||
}
|
||||
|
||||
// A racing resume may have cancelled us before we grabbed SR_lock
|
||||
// above. Or another external suspend request could be waiting for us
|
||||
// by the time we return from SR_lock()->wait(). The thread
|
||||
// that requested the suspension may already be trying to walk our
|
||||
// stack and if we return now, we can change the stack out from under
|
||||
// it. This would be a "bad thing (TM)" and cause the stack walker
|
||||
// to crash. We stay self-suspended until there are no more pending
|
||||
// external suspend requests.
|
||||
while (is_external_suspend()) {
|
||||
ret++;
|
||||
this->set_ext_suspended();
|
||||
|
||||
// _ext_suspended flag is cleared by java_resume()
|
||||
while (is_ext_suspended()) {
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Helper routine to set up the correct thread state before calling java_suspend_self.
|
||||
// This is called when regular thread-state transition helpers can't be used because
|
||||
// we can be in various states, in particular _thread_in_native_trans.
|
||||
// We have to set the thread state directly to _thread_blocked so that it will
|
||||
// be seen to be safepoint/handshake safe whilst suspended. This is also
|
||||
// necessary to allow a thread in is_ext_suspend_completed, that observed the
|
||||
// _thread_in_native_trans state, to proceed.
|
||||
// The problem with setting thread state directly is that a
|
||||
// safepoint could happen just after java_suspend_self() returns after being resumed,
|
||||
// and the VM thread will see the _thread_blocked state. So we must check for a safepoint
|
||||
// after restoring the state to make sure we won't leave while a safepoint is in progress.
|
||||
// However, not all initial-states are allowed when performing a safepoint check, as we
|
||||
// should never be blocking at a safepoint whilst in those states(*). Of these 'bad' states
|
||||
// only _thread_in_native is possible when executing this code (based on our two callers).
|
||||
// A thread that is _thread_in_native is already safepoint-safe and so it doesn't matter
|
||||
// whether the VMThread sees the _thread_blocked state, or the _thread_in_native state,
|
||||
// and so we don't need the explicit safepoint check.
|
||||
// (*) See switch statement in SafepointSynchronize::block() for thread states that are
|
||||
// allowed when performing a safepoint check.
|
||||
|
||||
void JavaThread::java_suspend_self_with_safepoint_check() {
|
||||
assert(this == Thread::current(), "invariant");
|
||||
JavaThreadState state = thread_state();
|
||||
|
||||
do {
|
||||
set_thread_state(_thread_blocked);
|
||||
java_suspend_self();
|
||||
// The current thread could have been suspended again. We have to check for
|
||||
// suspend after restoring the saved state. Without this the current thread
|
||||
// might return to _thread_in_Java and execute bytecodes for an arbitrary
|
||||
// long time.
|
||||
set_thread_state_fence(state);
|
||||
|
||||
if (state != _thread_in_native) {
|
||||
SafepointMechanism::process_if_requested(this);
|
||||
}
|
||||
} while (is_external_suspend());
|
||||
return this->handshake_state()->resume();
|
||||
}
|
||||
|
||||
// Wait for another thread to perform object reallocation and relocking on behalf of
|
||||
// this thread.
|
||||
// This method is very similar to JavaThread::java_suspend_self_with_safepoint_check()
|
||||
// and has the same callers. It also performs a raw thread state transition to
|
||||
// _thread_blocked and back again to the original state before returning. The current
|
||||
// thread is required to change to _thread_blocked in order to be seen to be
|
||||
// safepoint/handshake safe whilst suspended and only after becoming handshake safe,
|
||||
// the other thread can complete the handshake used to synchronize with this thread
|
||||
// and then perform the reallocation and relocking. We cannot use the thread state
|
||||
// transition helpers because we arrive here in various states and also because the
|
||||
// helpers indirectly call this method. After leaving _thread_blocked we have to
|
||||
// check for safepoint/handshake, except if _thread_in_native. The thread is safe
|
||||
// Raw thread state transition to _thread_blocked and back again to the original
|
||||
// state before returning are performed. The current thread is required to
|
||||
// change to _thread_blocked in order to be seen to be safepoint/handshake safe
|
||||
// whilst suspended and only after becoming handshake safe, the other thread can
|
||||
// complete the handshake used to synchronize with this thread and then perform
|
||||
// the reallocation and relocking. We cannot use the thread state transition
|
||||
// helpers because we arrive here in various states and also because the helpers
|
||||
// indirectly call this method. After leaving _thread_blocked we have to check
|
||||
// for safepoint/handshake, except if _thread_in_native. The thread is safe
|
||||
// without blocking then. Allowed states are enumerated in
|
||||
// SafepointSynchronize::block(). See also EscapeBarrier::sync_and_suspend_*()
|
||||
|
||||
|
@ -2099,10 +1828,6 @@ void JavaThread::wait_for_object_deoptimization() {
|
|||
bool spin_wait = os::is_MP();
|
||||
do {
|
||||
set_thread_state(_thread_blocked);
|
||||
// Check if _external_suspend was set in the previous loop iteration.
|
||||
if (is_external_suspend()) {
|
||||
java_suspend_self();
|
||||
}
|
||||
// Wait for object deoptimization if requested.
|
||||
if (spin_wait) {
|
||||
// A single deoptimization is typically very short. Microbenchmarks
|
||||
|
@ -2130,7 +1855,7 @@ void JavaThread::wait_for_object_deoptimization() {
|
|||
}
|
||||
// A handshake for obj. deoptimization suspend could have been processed so
|
||||
// we must check after processing.
|
||||
} while (is_obj_deopt_suspend() || is_external_suspend());
|
||||
} while (is_obj_deopt_suspend());
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
|
@ -2183,29 +1908,6 @@ void JavaThread::check_special_condition_for_native_trans(JavaThread *thread) {
|
|||
}
|
||||
}
|
||||
|
||||
// We need to guarantee the Threads_lock here, since resumes are not
|
||||
// allowed during safepoint synchronization
|
||||
// Can only resume from an external suspension
|
||||
void JavaThread::java_resume() {
|
||||
assert_locked_or_safepoint(Threads_lock);
|
||||
|
||||
// Sanity check: thread is gone, has started exiting or the thread
|
||||
// was not externally suspended.
|
||||
ThreadsListHandle tlh;
|
||||
if (!tlh.includes(this) || is_exiting() || !is_external_suspend()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
|
||||
clear_external_suspend();
|
||||
|
||||
if (is_ext_suspended()) {
|
||||
clear_ext_suspended();
|
||||
SR_lock()->notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Deoptimization
|
||||
// Function for testing deoptimization
|
||||
|
@ -2774,15 +2476,7 @@ bool JavaThread::sleep(jlong millis) {
|
|||
{
|
||||
ThreadBlockInVM tbivm(this);
|
||||
OSThreadWaitState osts(this->osthread(), false /* not Object.wait() */);
|
||||
|
||||
this->set_suspend_equivalent();
|
||||
// cleared by handle_special_suspend_equivalent_condition() or
|
||||
// java_suspend_self() via check_and_wait_while_suspended()
|
||||
|
||||
slp->park(millis);
|
||||
|
||||
// were we externally suspended while we were waiting?
|
||||
this->check_and_wait_while_suspended();
|
||||
}
|
||||
|
||||
// Update elapsed time tracking
|
||||
|
@ -3706,11 +3400,11 @@ void Threads::destroy_vm() {
|
|||
_vm_complete = false;
|
||||
#endif
|
||||
// Wait until we are the last non-daemon thread to execute
|
||||
{ MonitorLocker nu(Threads_lock);
|
||||
{
|
||||
MonitorLocker nu(Threads_lock);
|
||||
while (Threads::number_of_non_daemon_threads() > 1)
|
||||
// This wait should make safepoint checks, wait without a timeout,
|
||||
// and wait as a suspend-equivalent condition.
|
||||
nu.wait(0, Mutex::_as_suspend_equivalent_flag);
|
||||
// This wait should make safepoint checks, wait without a timeout.
|
||||
nu.wait(0);
|
||||
}
|
||||
|
||||
EventShutdown e;
|
||||
|
@ -3879,7 +3573,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) {
|
|||
// the thread might mess around with locks after this point. This can cause it
|
||||
// to do callbacks into the safepoint code. However, the safepoint code is not aware
|
||||
// of this thread since it is removed from the queue.
|
||||
p->set_terminated_value();
|
||||
p->set_terminated(JavaThread::_thread_terminated);
|
||||
|
||||
// Notify threads waiting in EscapeBarriers
|
||||
EscapeBarrier::thread_removed(p);
|
||||
|
|
|
@ -214,80 +214,11 @@ class Thread: public ThreadShadow {
|
|||
|
||||
protected:
|
||||
static void* allocate(size_t size, bool throw_excpt, MEMFLAGS flags = mtThread);
|
||||
private:
|
||||
|
||||
// ***************************************************************
|
||||
// Suspend and resume support
|
||||
// ***************************************************************
|
||||
//
|
||||
// VM suspend/resume no longer exists - it was once used for various
|
||||
// things including safepoints but was deprecated and finally removed
|
||||
// in Java 7. Because VM suspension was considered "internal" Java-level
|
||||
// suspension was considered "external", and this legacy naming scheme
|
||||
// remains.
|
||||
//
|
||||
// External suspend/resume requests come from JVM_SuspendThread,
|
||||
// JVM_ResumeThread, JVMTI SuspendThread, and finally JVMTI
|
||||
// ResumeThread. External
|
||||
// suspend requests cause _external_suspend to be set and external
|
||||
// resume requests cause _external_suspend to be cleared.
|
||||
// External suspend requests do not nest on top of other external
|
||||
// suspend requests. The higher level APIs reject suspend requests
|
||||
// for already suspended threads.
|
||||
//
|
||||
// The external_suspend
|
||||
// flag is checked by has_special_runtime_exit_condition() and java thread
|
||||
// will self-suspend when handle_special_runtime_exit_condition() is
|
||||
// called. Most uses of the _thread_blocked state in JavaThreads are
|
||||
// considered the same as being externally suspended; if the blocking
|
||||
// condition lifts, the JavaThread will self-suspend. Other places
|
||||
// where VM checks for external_suspend include:
|
||||
// + mutex granting (do not enter monitors when thread is suspended)
|
||||
// + state transitions from _thread_in_native
|
||||
//
|
||||
// In general, java_suspend() does not wait for an external suspend
|
||||
// request to complete. When it returns, the only guarantee is that
|
||||
// the _external_suspend field is true.
|
||||
//
|
||||
// wait_for_ext_suspend_completion() is used to wait for an external
|
||||
// suspend request to complete. External suspend requests are usually
|
||||
// followed by some other interface call that requires the thread to
|
||||
// be quiescent, e.g., GetCallTrace(). By moving the "wait time" into
|
||||
// the interface that requires quiescence, we give the JavaThread a
|
||||
// chance to self-suspend before we need it to be quiescent. This
|
||||
// improves overall suspend/query performance.
|
||||
//
|
||||
// _suspend_flags controls the behavior of java_ suspend/resume.
|
||||
// It must be set under the protection of SR_lock. Read from the flag is
|
||||
// OK without SR_lock as long as the value is only used as a hint.
|
||||
// (e.g., check _external_suspend first without lock and then recheck
|
||||
// inside SR_lock and finish the suspension)
|
||||
//
|
||||
// _suspend_flags is also overloaded for other "special conditions" so
|
||||
// that a single check indicates whether any special action is needed
|
||||
// eg. for async exceptions.
|
||||
// -------------------------------------------------------------------
|
||||
// Notes:
|
||||
// 1. The suspend/resume logic no longer uses ThreadState in OSThread
|
||||
// but we still update its value to keep other part of the system (mainly
|
||||
// JVMTI) happy. ThreadState is legacy code (see notes in
|
||||
// osThread.hpp).
|
||||
//
|
||||
// 2. It would be more natural if set_external_suspend() is private and
|
||||
// part of java_suspend(), but that probably would affect the suspend/query
|
||||
// performance. Need more investigation on this.
|
||||
|
||||
// suspend/resume lock: used for self-suspend
|
||||
Monitor* _SR_lock;
|
||||
|
||||
protected:
|
||||
enum SuspendFlags {
|
||||
// NOTE: avoid using the sign-bit as cc generates different test code
|
||||
// when the sign-bit is used, and sometimes incorrectly - see CR 6398077
|
||||
|
||||
_external_suspend = 0x20000000U, // thread is asked to self suspend
|
||||
_ext_suspended = 0x40000000U, // thread has self-suspended
|
||||
|
||||
_has_async_exception = 0x00000001U, // there is a pending async exception
|
||||
|
||||
_trace_flag = 0x00000004U, // call tracing backend
|
||||
|
@ -513,8 +444,6 @@ class Thread: public ThreadShadow {
|
|||
os::set_native_thread_name(name);
|
||||
}
|
||||
|
||||
Monitor* SR_lock() const { return _SR_lock; }
|
||||
|
||||
bool has_async_exception() const { return (_suspend_flags & _has_async_exception) != 0; }
|
||||
|
||||
inline void set_suspend_flag(SuspendFlags f);
|
||||
|
@ -810,9 +739,13 @@ protected:
|
|||
public:
|
||||
volatile intptr_t _Stalled;
|
||||
volatile int _TypeTag;
|
||||
ParkEvent * _ParkEvent; // for Object monitors, JVMTI raw monitors,
|
||||
ParkEvent * volatile _ParkEvent; // for Object monitors, JVMTI raw monitors,
|
||||
// and ObjectSynchronizer::read_stable_mark
|
||||
int NativeSyncRecursion; // diagnostic
|
||||
|
||||
// Termination indicator used by the signal handler.
|
||||
// _ParkEvent is just a convenient field we can NULL out after setting the JavaThread termination state
|
||||
// (which can't itself be read from the signal handler if a signal hits during the Thread destructor).
|
||||
bool has_terminated() { return Atomic::load(&_ParkEvent) == NULL; };
|
||||
|
||||
volatile int _OnTrap; // Resume-at IP delta
|
||||
jint _hashStateW; // Marsaglia Shift-XOR thread-local RNG
|
||||
|
@ -869,6 +802,7 @@ class JavaThread: public Thread {
|
|||
friend class JVMCIVMStructs;
|
||||
friend class WhiteBox;
|
||||
friend class ThreadsSMRSupport; // to access _threadObj for exiting_threads_oops_do
|
||||
friend class HandshakeState;
|
||||
private:
|
||||
bool _on_thread_list; // Is set when this JavaThread is added to the Threads list
|
||||
OopHandle _threadObj; // The Java level thread object
|
||||
|
@ -953,6 +887,7 @@ class JavaThread: public Thread {
|
|||
NOT_PRODUCT(bool _requires_cross_modify_fence;) // State used by VerifyCrossModifyFence
|
||||
|
||||
// JavaThread termination support
|
||||
public:
|
||||
enum TerminatedTypes {
|
||||
_not_terminated = 0xDEAD - 2,
|
||||
_thread_exiting, // JavaThread::exit() has been called for this thread
|
||||
|
@ -961,6 +896,7 @@ class JavaThread: public Thread {
|
|||
// only VM_Exit can set _vm_exited
|
||||
};
|
||||
|
||||
private:
|
||||
// In general a JavaThread's _terminated field transitions as follows:
|
||||
//
|
||||
// _not_terminated => _thread_exiting => _thread_terminated
|
||||
|
@ -968,8 +904,7 @@ class JavaThread: public Thread {
|
|||
// _vm_exited is a special value to cover the case of a JavaThread
|
||||
// executing native code after the VM itself is terminated.
|
||||
volatile TerminatedTypes _terminated;
|
||||
// suspend/resume support
|
||||
volatile bool _suspend_equivalent; // Suspend equivalent condition
|
||||
|
||||
jint _in_deopt_handler; // count of deoptimization
|
||||
// handlers thread is in
|
||||
volatile bool _doing_unsafe_access; // Thread may fault due to unsafe access
|
||||
|
@ -1180,8 +1115,7 @@ class JavaThread: public Thread {
|
|||
}
|
||||
bool is_terminated() const;
|
||||
void set_terminated(TerminatedTypes t);
|
||||
// special for Threads::remove() which is static:
|
||||
void set_terminated_value();
|
||||
|
||||
void block_if_vm_exited();
|
||||
|
||||
bool doing_unsafe_access() { return _doing_unsafe_access; }
|
||||
|
@ -1207,98 +1141,24 @@ class JavaThread: public Thread {
|
|||
}
|
||||
|
||||
// Suspend/resume support for JavaThread
|
||||
private:
|
||||
inline void set_ext_suspended();
|
||||
inline void clear_ext_suspended();
|
||||
bool java_suspend(); // higher-level suspension logic called by the public APIs
|
||||
bool java_resume(); // higher-level resume logic called by the public APIs
|
||||
bool is_suspended() { return _handshake.is_suspended(); }
|
||||
|
||||
public:
|
||||
void java_suspend(); // higher-level suspension logic called by the public APIs
|
||||
void java_resume(); // higher-level resume logic called by the public APIs
|
||||
int java_suspend_self(); // low-level self-suspension mechanics
|
||||
static void check_safepoint_and_suspend_for_native_trans(JavaThread *thread);
|
||||
// Check for async exception in addition to safepoint.
|
||||
static void check_special_condition_for_native_trans(JavaThread *thread);
|
||||
|
||||
// Whenever a thread transitions from native to vm/java it must suspend
|
||||
// if deopt suspend is present.
|
||||
bool is_suspend_after_native() const {
|
||||
return (_suspend_flags & (_obj_deopt JFR_ONLY(| _trace_flag))) != 0;
|
||||
}
|
||||
|
||||
// Synchronize with another thread that is deoptimizing objects of the
|
||||
// current thread, i.e. reverts optimizations based on escape analysis.
|
||||
void wait_for_object_deoptimization();
|
||||
|
||||
private:
|
||||
// mid-level wrapper around java_suspend_self to set up correct state and
|
||||
// check for a pending safepoint at the end
|
||||
void java_suspend_self_with_safepoint_check();
|
||||
|
||||
public:
|
||||
void check_and_wait_while_suspended() {
|
||||
assert(JavaThread::current() == this, "sanity check");
|
||||
|
||||
bool do_self_suspend;
|
||||
do {
|
||||
// were we externally suspended while we were waiting?
|
||||
do_self_suspend = handle_special_suspend_equivalent_condition();
|
||||
if (do_self_suspend) {
|
||||
// don't surprise the thread that suspended us by returning
|
||||
java_suspend_self();
|
||||
set_suspend_equivalent();
|
||||
}
|
||||
} while (do_self_suspend);
|
||||
}
|
||||
static void check_safepoint_and_suspend_for_native_trans(JavaThread *thread);
|
||||
// Check for async exception in addition to safepoint and suspend request.
|
||||
static void check_special_condition_for_native_trans(JavaThread *thread);
|
||||
|
||||
bool is_ext_suspend_completed();
|
||||
|
||||
inline void set_external_suspend();
|
||||
inline void clear_external_suspend();
|
||||
|
||||
bool is_external_suspend() const {
|
||||
return (_suspend_flags & _external_suspend) != 0;
|
||||
}
|
||||
// Whenever a thread transitions from native to vm/java it must suspend
|
||||
// if external|deopt suspend is present.
|
||||
bool is_suspend_after_native() const {
|
||||
return (_suspend_flags & (_external_suspend | _obj_deopt JFR_ONLY(| _trace_flag))) != 0;
|
||||
}
|
||||
|
||||
// external suspend request is completed
|
||||
bool is_ext_suspended() const {
|
||||
return (_suspend_flags & _ext_suspended) != 0;
|
||||
}
|
||||
|
||||
bool is_external_suspend_with_lock() const {
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
return is_external_suspend();
|
||||
}
|
||||
|
||||
// Special method to handle a pending external suspend request
|
||||
// when a suspend equivalent condition lifts.
|
||||
bool handle_special_suspend_equivalent_condition() {
|
||||
assert(is_suspend_equivalent(),
|
||||
"should only be called in a suspend equivalence condition");
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
bool ret = is_external_suspend();
|
||||
if (!ret) {
|
||||
// not about to self-suspend so clear suspend equivalence
|
||||
clear_suspend_equivalent();
|
||||
}
|
||||
// implied else:
|
||||
// We have a pending external suspend request so we leave the
|
||||
// suspend_equivalent flag set until java_suspend_self() sets
|
||||
// the ext_suspended flag and clears the suspend_equivalent
|
||||
// flag. This insures that wait_for_ext_suspend_completion()
|
||||
// will return consistent values.
|
||||
return ret;
|
||||
}
|
||||
|
||||
// utility methods to see if we are doing some kind of suspension
|
||||
bool is_being_ext_suspended() const {
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
return is_ext_suspended() || is_external_suspend();
|
||||
}
|
||||
|
||||
bool is_suspend_equivalent() const { return _suspend_equivalent; }
|
||||
|
||||
void set_suspend_equivalent() { _suspend_equivalent = true; }
|
||||
void clear_suspend_equivalent() { _suspend_equivalent = false; }
|
||||
|
||||
// Thread.stop support
|
||||
void send_thread_stop(oop throwable);
|
||||
AsyncRequests clear_special_runtime_exit_condition() {
|
||||
|
@ -1318,17 +1178,7 @@ class JavaThread: public Thread {
|
|||
// Return true if JavaThread has an asynchronous condition or
|
||||
// if external suspension is requested.
|
||||
bool has_special_runtime_exit_condition() {
|
||||
// Because we don't use is_external_suspend_with_lock
|
||||
// it is possible that we won't see an asynchronous external suspend
|
||||
// request that has just gotten started, i.e., SR_lock grabbed but
|
||||
// _external_suspend field change either not made yet or not visible
|
||||
// yet. However, this is okay because the request is asynchronous and
|
||||
// we will see the new flag value the next time through. It's also
|
||||
// possible that the external suspend request is dropped after
|
||||
// we have checked is_external_suspend(), we will recheck its value
|
||||
// under SR_lock in java_suspend_self().
|
||||
return (_special_runtime_exit_condition != _no_async_condition) ||
|
||||
is_external_suspend() || is_trace_suspend() || is_obj_deopt_suspend();
|
||||
return (_special_runtime_exit_condition != _no_async_condition) || is_trace_suspend() || is_obj_deopt_suspend();
|
||||
}
|
||||
|
||||
void set_pending_unsafe_access_error() { _special_runtime_exit_condition = _async_unsafe_access_error; }
|
||||
|
|
|
@ -119,20 +119,6 @@ inline WXMode Thread::enable_wx(WXMode new_state) {
|
|||
}
|
||||
#endif // __APPLE__ && AARCH64
|
||||
|
||||
inline void JavaThread::set_ext_suspended() {
|
||||
set_suspend_flag (_ext_suspended);
|
||||
}
|
||||
inline void JavaThread::clear_ext_suspended() {
|
||||
clear_suspend_flag(_ext_suspended);
|
||||
}
|
||||
|
||||
inline void JavaThread::set_external_suspend() {
|
||||
set_suspend_flag(_external_suspend);
|
||||
}
|
||||
inline void JavaThread::clear_external_suspend() {
|
||||
clear_suspend_flag(_external_suspend);
|
||||
}
|
||||
|
||||
inline void JavaThread::set_pending_async_exception(oop e) {
|
||||
_pending_async_exception = e;
|
||||
_special_runtime_exit_condition = _async_exception;
|
||||
|
@ -194,28 +180,20 @@ inline void JavaThread::set_done_attaching_via_jni() {
|
|||
inline bool JavaThread::is_exiting() const {
|
||||
// Use load-acquire so that setting of _terminated by
|
||||
// JavaThread::exit() is seen more quickly.
|
||||
TerminatedTypes l_terminated = (TerminatedTypes)
|
||||
Atomic::load_acquire((volatile jint *) &_terminated);
|
||||
TerminatedTypes l_terminated = Atomic::load_acquire(&_terminated);
|
||||
return l_terminated == _thread_exiting || check_is_terminated(l_terminated);
|
||||
}
|
||||
|
||||
inline bool JavaThread::is_terminated() const {
|
||||
// Use load-acquire so that setting of _terminated by
|
||||
// JavaThread::exit() is seen more quickly.
|
||||
TerminatedTypes l_terminated = (TerminatedTypes)
|
||||
Atomic::load_acquire((volatile jint *) &_terminated);
|
||||
TerminatedTypes l_terminated = Atomic::load_acquire(&_terminated);
|
||||
return check_is_terminated(l_terminated);
|
||||
}
|
||||
|
||||
inline void JavaThread::set_terminated(TerminatedTypes t) {
|
||||
// use release-store so the setting of _terminated is seen more quickly
|
||||
Atomic::release_store((volatile jint *) &_terminated, (jint) t);
|
||||
}
|
||||
|
||||
// special for Threads::remove() which is static:
|
||||
inline void JavaThread::set_terminated_value() {
|
||||
// use release-store so the setting of _terminated is seen more quickly
|
||||
Atomic::release_store((volatile jint *) &_terminated, (jint) _thread_terminated);
|
||||
Atomic::release_store(&_terminated, t);
|
||||
}
|
||||
|
||||
// Allow tracking of class initialization monitor use
|
||||
|
|
|
@ -2145,8 +2145,6 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
|||
/* Thread::SuspendFlags enum */ \
|
||||
/*****************************/ \
|
||||
\
|
||||
declare_constant(Thread::_external_suspend) \
|
||||
declare_constant(Thread::_ext_suspended) \
|
||||
declare_constant(Thread::_has_async_exception) \
|
||||
\
|
||||
/*******************/ \
|
||||
|
|
|
@ -309,7 +309,7 @@ static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args,
|
|||
|
||||
int thread_status = static_cast<int>(snapshot->thread_status());
|
||||
assert((thread_status & JMM_THREAD_STATE_FLAG_MASK) == 0, "Flags already set in thread_status in Thread object");
|
||||
if (snapshot->is_ext_suspended()) {
|
||||
if (snapshot->is_suspended()) {
|
||||
thread_status |= JMM_THREAD_STATE_FLAG_SUSPENDED;
|
||||
}
|
||||
if (snapshot->is_in_native()) {
|
||||
|
|
|
@ -879,7 +879,7 @@ void ThreadSnapshot::initialize(ThreadsList * t_list, JavaThread* thread) {
|
|||
_sleep_count = stat->sleep_count();
|
||||
|
||||
_thread_status = java_lang_Thread::get_thread_status(threadObj);
|
||||
_is_ext_suspended = thread->is_being_ext_suspended();
|
||||
_is_suspended = thread->is_suspended();
|
||||
_is_in_native = (thread->thread_state() == _thread_in_native);
|
||||
|
||||
Handle obj = ThreadService::get_current_contended_monitor(thread);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
|
@ -196,7 +196,7 @@ private:
|
|||
OopHandle _threadObj;
|
||||
JavaThreadStatus _thread_status;
|
||||
|
||||
bool _is_ext_suspended;
|
||||
bool _is_suspended;
|
||||
bool _is_in_native;
|
||||
|
||||
jlong _contended_enter_ticks;
|
||||
|
@ -229,7 +229,7 @@ public:
|
|||
|
||||
void set_next(ThreadSnapshot* n) { _next = n; }
|
||||
|
||||
bool is_ext_suspended() { return _is_ext_suspended; }
|
||||
bool is_suspended() { return _is_suspended; }
|
||||
bool is_in_native() { return _is_in_native; }
|
||||
|
||||
jlong contended_enter_count() { return _contended_enter_count; }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, 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
|
||||
|
@ -35,8 +35,6 @@ public class Thread extends VMObject {
|
|||
|
||||
private static CIntegerField suspendFlagsField;
|
||||
// Thread::SuspendFlags enum constants
|
||||
private static int EXTERNAL_SUSPEND;
|
||||
private static int EXT_SUSPENDED;
|
||||
private static int HAS_ASYNC_EXCEPTION;
|
||||
|
||||
private static AddressField activeHandlesField;
|
||||
|
@ -57,8 +55,6 @@ public class Thread extends VMObject {
|
|||
Type type = db.lookupType("Thread");
|
||||
|
||||
suspendFlagsField = type.getCIntegerField("_suspend_flags");
|
||||
EXTERNAL_SUSPEND = db.lookupIntConstant("Thread::_external_suspend").intValue();
|
||||
EXT_SUSPENDED = db.lookupIntConstant("Thread::_ext_suspended").intValue();
|
||||
HAS_ASYNC_EXCEPTION = db.lookupIntConstant("Thread::_has_async_exception").intValue();
|
||||
|
||||
tlabFieldOffset = type.getField("_tlab").getOffset();
|
||||
|
@ -76,23 +72,6 @@ public class Thread extends VMObject {
|
|||
return (int) suspendFlagsField.getValue(addr);
|
||||
}
|
||||
|
||||
public boolean isExternalSuspend() {
|
||||
return (suspendFlags() & EXTERNAL_SUSPEND) != 0;
|
||||
}
|
||||
|
||||
public boolean isExtSuspended() {
|
||||
return (suspendFlags() & EXT_SUSPENDED) != 0;
|
||||
}
|
||||
|
||||
public boolean isBeingExtSuspended() {
|
||||
return isExtSuspended() || isExternalSuspend();
|
||||
}
|
||||
|
||||
// historical usage: checked for VM or external suspension
|
||||
public boolean isAnySuspended() {
|
||||
return isExtSuspended();
|
||||
}
|
||||
|
||||
public boolean hasAsyncException() {
|
||||
return (suspendFlags() & HAS_ASYNC_EXCEPTION) != 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2021, 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
|
||||
|
@ -103,9 +103,6 @@ public:
|
|||
MutexLocker ml(Threads_lock);
|
||||
Threads::add(this);
|
||||
}
|
||||
{
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void main_run() = 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2021, 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
|
||||
|
@ -129,8 +129,8 @@ public class SuspendWithCurrentThread {
|
|||
" to suspend all tested threads including itself");
|
||||
ThreadToSuspend.setAllThreadsReady();
|
||||
|
||||
if (!checkSuspendedStatus()) {
|
||||
throw new RuntimeException("Main: FAILED status returned from checkTestedThreadsSuspended");
|
||||
while (!checkSuspendedStatus()) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
|
||||
log("Main: resuming all tested threads");
|
||||
|
@ -166,7 +166,6 @@ public class SuspendWithCurrentThread {
|
|||
class ThreadToSuspend extends Thread {
|
||||
private static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
private static native void init();
|
||||
private static native void suspendTestedThreads();
|
||||
private static volatile boolean allThreadsReady = false;
|
||||
|
||||
|
@ -187,10 +186,6 @@ class ThreadToSuspend extends Thread {
|
|||
// run thread continuously
|
||||
public void run() {
|
||||
boolean needSuspend = true;
|
||||
|
||||
if (isSuspender) {
|
||||
init();
|
||||
}
|
||||
threadReady = true;
|
||||
|
||||
// run in a loop
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2021, 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
|
||||
|
@ -29,7 +29,6 @@ extern "C" {
|
|||
static jvmtiEnv* jvmti = NULL;
|
||||
static jthread* threads = NULL;
|
||||
static jsize threads_count = 0;
|
||||
static jrawMonitorID agent_monitor = NULL;
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
|
@ -46,18 +45,6 @@ check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
agent_lock(JNIEnv* jni) {
|
||||
jvmtiError err = jvmti->RawMonitorEnter(agent_monitor);
|
||||
check_jvmti_status(jni, err, "monitor_enter: error in JVMTI RawMonitorEnter");
|
||||
}
|
||||
|
||||
static void
|
||||
agent_unlock(JNIEnv* jni) {
|
||||
jvmtiError err = jvmti->RawMonitorExit(agent_monitor);
|
||||
check_jvmti_status(jni, err, "monitor_exit: error in JVMTI RawMonitorExit");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_SuspendWithCurrentThread_registerTestedThreads(JNIEnv *jni, jclass cls, jobjectArray threadsArr) {
|
||||
LOG("\nregisterTestedThreads: started");
|
||||
|
@ -74,16 +61,6 @@ Java_SuspendWithCurrentThread_registerTestedThreads(JNIEnv *jni, jclass cls, job
|
|||
LOG("registerTestedThreads: finished\n");
|
||||
}
|
||||
|
||||
/* This function is executed on the suspender thread, not the Main thread */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadToSuspend_init(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError err = jvmti->CreateRawMonitor("Agent monitor", &agent_monitor);
|
||||
check_jvmti_status(jni, err, "Java_ThreadToSuspend_init: error in JVMTI CreateRawMonitor");
|
||||
|
||||
// Main thread has to wait for the suspender thread to complete tested threads suspension
|
||||
agent_lock(jni);
|
||||
}
|
||||
|
||||
/* This function is executed on the suspender thread which is not Main thread */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadToSuspend_suspendTestedThreads(JNIEnv *jni, jclass cls) {
|
||||
|
@ -106,9 +83,6 @@ Java_ThreadToSuspend_suspendTestedThreads(JNIEnv *jni, jclass cls) {
|
|||
}
|
||||
LOG("suspendTestedThreads: finished\n");
|
||||
|
||||
// Allow the Main thread to inspect the result of tested threads suspension
|
||||
agent_unlock(jni);
|
||||
|
||||
err = jvmti->Deallocate((unsigned char*)results);
|
||||
check_jvmti_status(jni, err, "suspendTestedThreads: error in JVMTI Deallocate results");
|
||||
}
|
||||
|
@ -117,10 +91,6 @@ JNIEXPORT jboolean JNICALL
|
|||
Java_SuspendWithCurrentThread_checkTestedThreadsSuspended(JNIEnv *jni, jclass cls) {
|
||||
LOG("checkTestedThreadsSuspended: started");
|
||||
|
||||
// Block until the suspender thread competes the tested threads suspension
|
||||
agent_lock(jni);
|
||||
agent_unlock(jni);
|
||||
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
jint state = 0;
|
||||
jvmtiError err = jvmti->GetThreadState(threads[i], &state);
|
||||
|
@ -129,7 +99,7 @@ Java_SuspendWithCurrentThread_checkTestedThreadsSuspended(JNIEnv *jni, jclass cl
|
|||
if ((state & JVMTI_THREAD_STATE_SUSPENDED) == 0) {
|
||||
LOG("thread #%d has not been suspended yet: "
|
||||
"# state: (%#x)", i, (int)state);
|
||||
jni->FatalError("checkTestedThreadsSuspended: error: expected all tested threads suspended");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
LOG("checkTestedThreadsSuspended: finished\n");
|
||||
|
@ -167,8 +137,6 @@ Java_SuspendWithCurrentThread_releaseTestedThreadsInfo(JNIEnv *jni, jclass cls)
|
|||
jvmtiError err;
|
||||
|
||||
LOG("\nreleaseTestedThreadsInfo: started");
|
||||
err = jvmti->DestroyRawMonitor(agent_monitor);
|
||||
check_jvmti_status(jni, err, "releaseTestedThreadsInfo: error in JVMTI DestroyRawMonitor");
|
||||
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
if (threads[i] != NULL) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue