8293592: Remove JVM_StopThread, stillborn, and related cleanup

Reviewed-by: alanb, sspitsyn, rehn, coleenp
This commit is contained in:
David Holmes 2022-09-27 21:03:50 +00:00
parent 739fdec7ea
commit 763d4bf074
18 changed files with 60 additions and 187 deletions

View File

@ -204,7 +204,6 @@ JVM_SetStackWalkContinuation
JVM_SetThreadPriority
JVM_Sleep
JVM_StartThread
JVM_StopThread
JVM_SupportsCX8
JVM_TotalMemory
JVM_UnloadLibrary

View File

@ -1642,7 +1642,6 @@ void java_lang_Class::set_classRedefinedCount(oop the_class_mirror, int value) {
int java_lang_Thread_FieldHolder::_group_offset;
int java_lang_Thread_FieldHolder::_priority_offset;
int java_lang_Thread_FieldHolder::_stackSize_offset;
int java_lang_Thread_FieldHolder::_stillborn_offset;
int java_lang_Thread_FieldHolder::_daemon_offset;
int java_lang_Thread_FieldHolder::_thread_status_offset;
@ -1650,7 +1649,6 @@ int java_lang_Thread_FieldHolder::_thread_status_offset;
macro(_group_offset, k, vmSymbols::group_name(), threadgroup_signature, false); \
macro(_priority_offset, k, vmSymbols::priority_name(), int_signature, false); \
macro(_stackSize_offset, k, "stackSize", long_signature, false); \
macro(_stillborn_offset, k, "stillborn", bool_signature, false); \
macro(_daemon_offset, k, vmSymbols::daemon_name(), bool_signature, false); \
macro(_thread_status_offset, k, "threadStatus", int_signature, false)
@ -1683,14 +1681,6 @@ jlong java_lang_Thread_FieldHolder::stackSize(oop holder) {
return holder->long_field(_stackSize_offset);
}
bool java_lang_Thread_FieldHolder::is_stillborn(oop holder) {
return holder->bool_field(_stillborn_offset) != 0;
}
void java_lang_Thread_FieldHolder::set_stillborn(oop holder) {
holder->bool_field_put(_stillborn_offset, true);
}
bool java_lang_Thread_FieldHolder::is_daemon(oop holder) {
return holder->bool_field(_daemon_offset) != 0;
}
@ -1854,21 +1844,6 @@ oop java_lang_Thread::threadGroup(oop java_thread) {
}
bool java_lang_Thread::is_stillborn(oop java_thread) {
oop holder = java_lang_Thread::holder(java_thread);
assert(holder != NULL, "Java Thread not initialized");
return java_lang_Thread_FieldHolder::is_stillborn(holder);
}
// We never have reason to turn the stillborn bit off
void java_lang_Thread::set_stillborn(oop java_thread) {
oop holder = java_lang_Thread::holder(java_thread);
assert(holder != NULL, "Java Thread not initialized");
java_lang_Thread_FieldHolder::set_stillborn(holder);
}
bool java_lang_Thread::is_alive(oop java_thread) {
JavaThread* thr = java_lang_Thread::thread(java_thread);
return (thr != NULL);

View File

@ -379,9 +379,6 @@ class java_lang_Thread : AllStatic {
static void set_priority(oop java_thread, ThreadPriority priority);
// Thread group
static oop threadGroup(oop java_thread);
// Stillborn
static bool is_stillborn(oop java_thread);
static void set_stillborn(oop java_thread);
// Alive (NOTE: this is not really a field, but provides the correct
// definition without doing a Java call)
static bool is_alive(oop java_thread);
@ -434,7 +431,6 @@ class java_lang_Thread_FieldHolder : AllStatic {
static int _group_offset;
static int _priority_offset;
static int _stackSize_offset;
static int _stillborn_offset;
static int _daemon_offset;
static int _thread_status_offset;
@ -450,9 +446,6 @@ class java_lang_Thread_FieldHolder : AllStatic {
static jlong stackSize(oop holder);
static bool is_stillborn(oop holder);
static void set_stillborn(oop holder);
static bool is_daemon(oop holder);
static void set_daemon(oop holder);

View File

@ -58,7 +58,6 @@
do_klass(System_klass, java_lang_System ) \
do_klass(Throwable_klass, java_lang_Throwable ) \
do_klass(Error_klass, java_lang_Error ) \
do_klass(ThreadDeath_klass, java_lang_ThreadDeath ) \
do_klass(Exception_klass, java_lang_Exception ) \
do_klass(RuntimeException_klass, java_lang_RuntimeException ) \
do_klass(SecurityManager_klass, java_lang_SecurityManager ) \
@ -197,4 +196,3 @@
/*end*/
#endif // SHARE_CLASSFILE_VMCLASSMACROS_HPP

View File

@ -70,7 +70,6 @@
template(java_lang_Cloneable, "java/lang/Cloneable") \
template(java_lang_Throwable, "java/lang/Throwable") \
template(java_lang_ClassLoader, "java/lang/ClassLoader") \
template(java_lang_ThreadDeath, "java/lang/ThreadDeath") \
template(java_lang_Runnable, "java/lang/Runnable") \
template(jdk_internal_vm_ContinuationScope, "jdk/internal/vm/ContinuationScope") \
template(jdk_internal_vm_StackChunk, "jdk/internal/vm/StackChunk") \
@ -394,7 +393,6 @@
template(main_name, "main") \
template(name_name, "name") \
template(priority_name, "priority") \
template(stillborn_name, "stillborn") \
template(group_name, "group") \
template(daemon_name, "daemon") \
template(run_method_name, "run") \

View File

@ -266,9 +266,6 @@ JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, job
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);
JNIEXPORT void JNICALL
JVM_StopThread(JNIEnv *env, jobject thread, jobject exception);
JNIEXPORT jboolean JNICALL
JVM_IsThreadAlive(JNIEnv *env, jobject thread);

View File

@ -776,11 +776,7 @@ JRT_ENTRY(void, InterpreterRuntime::new_illegal_monitor_state_exception(JavaThre
Handle exception(current, current->vm_result());
assert(exception() != NULL, "vm result should be set");
current->set_vm_result(NULL); // clear vm result before continuing (may cause memory leaks and assert failures)
if (!exception->is_a(vmClasses::ThreadDeath_klass())) {
exception = get_preinitialized_exception(
vmClasses::IllegalMonitorStateException_klass(),
CATCH);
}
exception = get_preinitialized_exception(vmClasses::IllegalMonitorStateException_klass(), CATCH);
current->set_vm_result(exception());
JRT_END

View File

@ -1580,14 +1580,10 @@ void JVMCIRuntime::describe_pending_hotspot_exception(JavaThread* THREAD, bool c
const char* exception_file = THREAD->exception_file();
int exception_line = THREAD->exception_line();
CLEAR_PENDING_EXCEPTION;
if (exception->is_a(vmClasses::ThreadDeath_klass())) {
// Don't print anything if we are being killed.
} else {
java_lang_Throwable::print_stack_trace(exception, tty);
java_lang_Throwable::print_stack_trace(exception, tty);
// Clear and ignore any exceptions raised during printing
CLEAR_PENDING_EXCEPTION;
}
// Clear and ignore any exceptions raised during printing
CLEAR_PENDING_EXCEPTION;
if (!clear) {
THREAD->set_pending_exception(exception(), exception_file, exception_line);
}

View File

@ -575,35 +575,31 @@ JNI_ENTRY_NO_PRESERVE(void, jni_ExceptionDescribe(JNIEnv *env))
if (thread->has_pending_exception()) {
Handle ex(thread, thread->pending_exception());
thread->clear_pending_exception();
if (ex->is_a(vmClasses::ThreadDeath_klass())) {
// Don't print anything if we are being killed.
jio_fprintf(defaultStream::error_stream(), "Exception ");
if (thread != NULL && thread->threadObj() != NULL) {
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(),
"in thread \"%s\" ", thread->name());
}
if (ex->is_a(vmClasses::Throwable_klass())) {
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
ex,
vmClasses::Throwable_klass(),
vmSymbols::printStackTrace_name(),
vmSymbols::void_method_signature(),
THREAD);
// If an exception is thrown in the call it gets thrown away. Not much
// we can do with it. The native code that calls this, does not check
// for the exception - hence, it might still be in the thread when DestroyVM gets
// called, potentially causing a few asserts to trigger - since no pending exception
// is expected.
CLEAR_PENDING_EXCEPTION;
} else {
jio_fprintf(defaultStream::error_stream(), "Exception ");
if (thread != NULL && thread->threadObj() != NULL) {
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(),
"in thread \"%s\" ", thread->name());
}
if (ex->is_a(vmClasses::Throwable_klass())) {
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
ex,
vmClasses::Throwable_klass(),
vmSymbols::printStackTrace_name(),
vmSymbols::void_method_signature(),
THREAD);
// If an exception is thrown in the call it gets thrown away. Not much
// we can do with it. The native code that calls this, does not check
// for the exception - hence, it might still be in the thread when DestroyVM gets
// called, potentially causing a few asserts to trigger - since no pending exception
// is expected.
CLEAR_PENDING_EXCEPTION;
} else {
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(),
". Uncaught exception of type %s.",
ex->klass()->external_name());
}
ResourceMark rm(THREAD);
jio_fprintf(defaultStream::error_stream(),
". Uncaught exception of type %s.",
ex->klass()->external_name());
}
}

View File

@ -2926,9 +2926,6 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// Allocate the C++ Thread structure and create the native thread. The
@ -2981,45 +2978,6 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVM_END
// JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints
// before the quasi-asynchronous exception is delivered. This is a little obtrusive,
// but is thought to be reliable and simple. In the case, where the receiver is the
// same thread as the sender, no VM_Operation is needed.
JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable))
ThreadsListHandle tlh(thread);
oop java_throwable = JNIHandles::resolve(throwable);
if (java_throwable == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
oop java_thread = NULL;
JavaThread* receiver = NULL;
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
Events::log_exception(thread,
"JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]",
p2i(receiver), p2i(java_thread), p2i(throwable));
if (is_alive) {
// jthread refers to a live JavaThread.
if (thread == receiver) {
// Exception is getting thrown at self so no VM_Operation needed.
THROW_OOP(java_throwable);
} else {
// Use a VM_Operation to throw the exception.
JavaThread::send_async_exception(receiver, java_throwable);
}
} else {
// Either:
// - target thread has not been started before being stopped, or
// - target thread already terminated
// We could read the threadStatus to determine which case it is
// but that is overkill as it doesn't matter. We must set the
// stillborn flag for the first case, and if the thread has already
// exited setting this flag has no effect.
java_lang_Thread::set_stillborn(java_thread);
}
JVM_END
JVM_ENTRY(jboolean, JVM_IsThreadAlive(JNIEnv* env, jobject jthread))
oop thread_oop = JNIHandles::resolve_non_null(jthread);
return java_lang_Thread::is_alive(thread_oop);
@ -3070,7 +3028,7 @@ JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
ThreadState old_state = thread->osthread()->get_state();
thread->osthread()->set_state(SLEEPING);
if (!thread->sleep(millis)) { // interrupted
// An asynchronous exception (e.g., ThreadDeathException) could have been thrown on
// An asynchronous exception could have been thrown on
// us while we were sleeping. We do not overwrite those.
if (!HAS_PENDING_EXCEPTION) {
HOTSPOT_THREAD_SLEEP_END(1);

View File

@ -83,7 +83,6 @@ class HandshakeOperation : public CHeapObj<mtThread> {
bool is_async() { return _handshake_cl->is_async(); }
bool is_suspend() { return _handshake_cl->is_suspend(); }
bool is_async_exception() { return _handshake_cl->is_async_exception(); }
bool is_ThreadDeath() { return _handshake_cl->is_ThreadDeath(); }
};
class AsyncHandshakeOperation : public HandshakeOperation {
@ -445,9 +444,6 @@ static bool no_async_exception_filter(HandshakeOperation* op) {
static bool async_exception_filter(HandshakeOperation* op) {
return op->is_async_exception();
}
static bool is_ThreadDeath_filter(HandshakeOperation* op) {
return op->is_ThreadDeath();
}
static bool no_suspend_no_async_exception_filter(HandshakeOperation* op) {
return !op->is_suspend() && !op->is_async_exception();
}
@ -503,18 +499,14 @@ bool HandshakeState::has_operation(bool allow_suspend, bool check_async_exceptio
return get_op_for_self(allow_suspend, check_async_exception) != NULL;
}
bool HandshakeState::has_async_exception_operation(bool ThreadDeath_only) {
bool HandshakeState::has_async_exception_operation() {
if (!has_operation()) return false;
MutexLocker ml(_lock.owned_by_self() ? NULL : &_lock, Mutex::_no_safepoint_check_flag);
if (!ThreadDeath_only) {
return _queue.peek(async_exception_filter) != NULL;
} else {
return _queue.peek(is_ThreadDeath_filter) != NULL;
}
return _queue.peek(async_exception_filter) != NULL;
}
void HandshakeState::clean_async_exception_operation() {
while (has_async_exception_operation(/* ThreadDeath_only */ false)) {
while (has_async_exception_operation()) {
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
HandshakeOperation* op;
op = _queue.peek(async_exception_filter);

View File

@ -54,7 +54,6 @@ class HandshakeClosure : public ThreadClosure, public CHeapObj<mtThread> {
virtual bool is_async() { return false; }
virtual bool is_suspend() { return false; }
virtual bool is_async_exception() { return false; }
virtual bool is_ThreadDeath() { return false; }
virtual void do_thread(Thread* thread) = 0;
};
@ -132,7 +131,7 @@ class HandshakeState {
bool has_operation() { return !_queue.is_empty(); }
bool has_operation(bool allow_suspend, bool check_async_exception);
bool has_async_exception_operation(bool ThreadDeath_only);
bool has_async_exception_operation();
void clean_async_exception_operation();
bool operation_pending(HandshakeOperation* op);

View File

@ -687,11 +687,9 @@ void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(_threadObj.peek() != NULL, "just checking");
// Execute thread entry point unless this thread has a pending exception
// or has been stopped before starting.
// Note: Due to JVM_StopThread we can have pending exceptions already!
if (!this->has_pending_exception() &&
!java_lang_Thread::is_stillborn(this->threadObj())) {
// Execute thread entry point unless this thread has a pending exception.
// Note: Due to JVMTI StopThread we can have pending exceptions already!
if (!this->has_pending_exception()) {
{
ResourceMark rm(this);
this->set_native_thread_name(this->name());
@ -719,15 +717,13 @@ static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), JavaThreadStatus::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
// Ignore pending exception, since we are exiting anyway
thread->clear_pending_exception();
}
@ -1067,29 +1063,25 @@ void JavaThread::handle_async_exception(oop java_throwable) {
}
}
// Only overwrite an already pending exception if it is not a ThreadDeath.
if (!has_pending_exception() || !pending_exception()->is_a(vmClasses::ThreadDeath_klass())) {
// We cannot call Exceptions::_throw(...) here because we cannot block
set_pending_exception(java_throwable, __FILE__, __LINE__);
// We cannot call Exceptions::_throw(...) here because we cannot block
set_pending_exception(java_throwable, __FILE__, __LINE__);
// Clear any extent-local bindings
set_extentLocalCache(NULL);
oop threadOop = threadObj();
assert(threadOop != NULL, "must be");
java_lang_Thread::clear_extentLocalBindings(threadOop);
// Clear any extent-local bindings on ThreadDeath
set_extentLocalCache(NULL);
oop threadOop = threadObj();
assert(threadOop != NULL, "must be");
java_lang_Thread::clear_extentLocalBindings(threadOop);
LogTarget(Info, exceptions) lt;
if (lt.is_enabled()) {
ResourceMark rm;
LogStream ls(lt);
ls.print("Async. exception installed at runtime exit (" INTPTR_FORMAT ")", p2i(this));
if (has_last_Java_frame()) {
frame f = last_frame();
ls.print(" (pc: " INTPTR_FORMAT " sp: " INTPTR_FORMAT " )", p2i(f.pc()), p2i(f.sp()));
}
ls.print_cr(" of type: %s", java_throwable->klass()->external_name());
LogTarget(Info, exceptions) lt;
if (lt.is_enabled()) {
ResourceMark rm;
LogStream ls(lt);
ls.print("Async. exception installed at runtime exit (" INTPTR_FORMAT ")", p2i(this));
if (has_last_Java_frame()) {
frame f = last_frame();
ls.print(" (pc: " INTPTR_FORMAT " sp: " INTPTR_FORMAT " )", p2i(f.pc()), p2i(f.sp()));
}
ls.print_cr(" of type: %s", java_throwable->klass()->external_name());
}
}
@ -1101,16 +1093,6 @@ void JavaThread::install_async_exception(AsyncExceptionHandshake* aeh) {
return;
}
// Don't install a new pending async exception if there is already
// a pending ThreadDeath one. Just interrupt thread from potential
// wait()/sleep()/park() and return.
if (has_async_exception_condition(true /* ThreadDeath_only */)) {
java_lang_Thread::set_interrupted(threadObj(), true);
this->interrupt();
delete aeh;
return;
}
oop exception = aeh->exception();
Handshake::execute(aeh, this); // Install asynchronous handshake

View File

@ -222,7 +222,7 @@ class JavaThread: public Thread {
void install_async_exception(AsyncExceptionHandshake* aec = NULL);
void handle_async_exception(oop java_throwable);
public:
bool has_async_exception_condition(bool ThreadDeath_only = false);
bool has_async_exception_condition();
inline void set_pending_unsafe_access_error();
static void send_async_exception(JavaThread* jt, oop java_throwable);

View File

@ -79,12 +79,9 @@ inline void JavaThread::clear_carrier_thread_suspended() {
class AsyncExceptionHandshake : public AsyncHandshakeClosure {
OopHandle _exception;
bool _is_ThreadDeath;
public:
AsyncExceptionHandshake(OopHandle& o, const char* name = "AsyncExceptionHandshake")
: AsyncHandshakeClosure(name), _exception(o) {
_is_ThreadDeath = exception()->is_a(vmClasses::ThreadDeath_klass());
}
: AsyncHandshakeClosure(name), _exception(o) { }
~AsyncExceptionHandshake() {
Thread* current = Thread::current();
@ -108,7 +105,6 @@ class AsyncExceptionHandshake : public AsyncHandshakeClosure {
return _exception.resolve();
}
bool is_async_exception() { return true; }
bool is_ThreadDeath() { return _is_ThreadDeath; }
};
class UnsafeAccessErrorHandshake : public AsyncHandshakeClosure {
@ -129,8 +125,8 @@ inline void JavaThread::set_pending_unsafe_access_error() {
}
}
inline bool JavaThread::has_async_exception_condition(bool ThreadDeath_only) {
return handshake_state()->has_async_exception_operation(ThreadDeath_only);
inline bool JavaThread::has_async_exception_condition() {
return handshake_state()->has_async_exception_operation();
}
inline JavaThread::NoAsyncExceptionDeliveryMark::NoAsyncExceptionDeliveryMark(JavaThread *t) : _target(t) {

View File

@ -74,8 +74,7 @@ void ThreadShadow::clear_pending_exception() {
void ThreadShadow::clear_pending_nonasync_exception() {
// Do not clear probable async exceptions.
if (!_pending_exception->is_a(vmClasses::ThreadDeath_klass()) &&
(_pending_exception->klass() != vmClasses::InternalError_klass() ||
if ((_pending_exception->klass() != vmClasses::InternalError_klass() ||
java_lang_InternalError::during_unsafe_access(_pending_exception) != JNI_TRUE)) {
clear_pending_exception();
}
@ -427,7 +426,7 @@ void Exceptions::wrap_dynamic_exception(bool is_indy, JavaThread* THREAD) {
// in JVMS 6.5.
if (exception->is_a(vmClasses::Error_klass())) {
// Pass through an Error, including BootstrapMethodError, any other form
// of linkage error, or say ThreadDeath/OutOfMemoryError
// of linkage error, or say OutOfMemoryError
if (ls != NULL) {
ls->print_cr("bootstrap method invocation wraps BSME around " PTR_FORMAT, p2i(exception));
exception->print_on(ls);

View File

@ -237,7 +237,7 @@ class Exceptions {
#define CHECK_AND_CLEAR_NULL CHECK_AND_CLEAR_(NULL)
#define CHECK_AND_CLEAR_false CHECK_AND_CLEAR_(false)
// CAUTION: These macros clears all exceptions except probable async exceptions j.l.InternalError and j.l.ThreadDeath.
// CAUTION: These macros clears all exceptions except probable async exceptions j.l.InternalError.
// So use it with caution.
#define CLEAR_PENDING_NONASYNC_EXCEPTION (((ThreadShadow*)THREAD)->clear_pending_nonasync_exception())
#define CHECK_AND_CLEAR_NONASYNC THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_NONASYNC_EXCEPTION; return; } (void)(0

View File

@ -247,7 +247,6 @@ public class Thread implements Runnable {
volatile int priority;
volatile boolean daemon;
volatile int threadStatus;
boolean stillborn;
FieldHolder(ThreadGroup group,
Runnable task,