8257831: Suspend with handshakes

Reviewed-by: dcubed, rrich, dholmes, pchilanomate, sspitsyn
This commit is contained in:
Robbin Ehn 2021-04-22 10:30:47 +00:00
parent 28af31db34
commit 86bd44fe80
40 changed files with 470 additions and 1081 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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() {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();
}
}
}

View file

@ -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) \

View file

@ -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

View file

@ -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_exiting()) { // thread is in the process of exiting
return (JVMTI_ERROR_THREAD_NOT_ALIVE);
}
java_thread->set_external_suspend();
if (java_thread->is_suspended()) {
return JVMTI_ERROR_THREAD_SUSPENDED;
}
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.
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;
continue;
}
java_thread->set_external_suspend();
if (java_thread->is_suspended()) {
results[i] = JVMTI_ERROR_THREAD_SUSPENDED;
continue;
}
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++;
if (java_thread == current) {
self_index = i;
continue;
}
if (!JvmtiSuspendControl::suspend(java_thread)) {
// 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;
}

View file

@ -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;
}

View file

@ -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 true;
return java_thread->java_resume();
}
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");
}

View file

@ -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();
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->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);
}
// guarded by SR_lock to avoid racing with new external suspend requests.
contended = Atomic::cmpxchg(&_owner, (Thread*)NULL, jt);
jt->SR_lock()->unlock();
} 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,29 +420,22 @@ 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();
}
if (jt->is_interrupted(true)) {
ret = M_INTERRUPTED;
}
}
simple_enter(jt);
}
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 {

View file

@ -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.

View file

@ -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;

View file

@ -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) },

View file

@ -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)") \
\

View file

@ -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);
op->do_handshake(_handshakee);
if (async) {
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);
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;
}

View file

@ -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
@ -43,9 +45,9 @@ class HandshakeClosure : public ThreadClosure, public CHeapObj<mtThread> {
const char* const _name;
public:
HandshakeClosure(const char* name) : _name(name) {}
virtual ~HandshakeClosure() {}
const char* name() const { return _name; }
virtual bool is_async() { return false; }
virtual ~HandshakeClosure() {}
const char* name() const { return _name; }
virtual bool is_async() { return false; }
virtual void do_thread(Thread* thread) = 0;
};
@ -61,33 +63,49 @@ class Handshake : public AllStatic {
// Execution of handshake operation
static void execute(HandshakeClosure* hs_cl);
static void execute(HandshakeClosure* hs_cl, JavaThread* target);
static void execute(AsyncHandshakeClosure* hs_cl, JavaThread* target);
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

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
// 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;
exit(current, false /* not_suspended */);
current->java_suspend_self();
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 */);
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);
}
} // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm
current->set_thread_state(_thread_in_vm);
}
// Node may be on the WaitSet, the EntryList (or cxq), or in transition
// from the WaitSet to the EntryList.

View file

@ -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);

View file

@ -77,24 +77,26 @@ void SafepointMechanism::default_initialize() {
}
void SafepointMechanism::process(JavaThread *thread) {
if (global_poll()) {
// 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);
}
bool need_rechecking;
do {
if (global_poll()) {
// 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);
}
// 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:
// 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);
// The call to on_safepoint fixes the thread's oops and the first few frames.
//
// 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);

View file

@ -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();
}
}

View file

@ -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()) {
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.
}
// 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);
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
ThreadService::current_thread_exiting(this, is_daemon(threadObj()));
} 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;
}
{ MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
if (!is_external_suspend()) {
// a racing resume has cancelled us; bail out now
return;
}
// 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);
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();
}
// 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;
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;
}
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);

View file

@ -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; }

View file

@ -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

View file

@ -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) \
\
/*******************/ \

View file

@ -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()) {

View file

@ -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);

View file

@ -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; }

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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) {