Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8263191: Consolidate ThreadInVMfromJavaNoAsyncException and ThreadBlo…
…ckInVMWithDeadlockCheck with existing wrappers

Reviewed-by: coleenp, dholmes, dcubed
  • Loading branch information
pchilano committed Mar 15, 2021
1 parent 80cdf78 commit d6b5e18
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 80 deletions.
98 changes: 26 additions & 72 deletions src/hotspot/share/runtime/interfaceSupport.inline.hpp
Expand Up @@ -150,17 +150,19 @@ class ThreadInVMForHandshake : public ThreadStateTransition {
};

class ThreadInVMfromJava : public ThreadStateTransition {
bool _check_asyncs;
public:
ThreadInVMfromJava(JavaThread* thread) : ThreadStateTransition(thread) {
ThreadInVMfromJava(JavaThread* thread, bool check_asyncs = true) : ThreadStateTransition(thread), _check_asyncs(check_asyncs) {
trans_from_java(_thread_in_vm);
}
~ThreadInVMfromJava() {
if (_thread->stack_overflow_state()->stack_yellow_reserved_zone_disabled()) {
_thread->stack_overflow_state()->enable_stack_yellow_reserved_zone();
}
trans(_thread_in_vm, _thread_in_Java);
// Check for pending. async. exceptions or suspends.
if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition();
// We prevent asynchronous exceptions from being installed on return to Java in situations
// where we can't tolerate them. See bugs: 4324348, 4854693, 4998314, 5040492, 5050705.
if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(_check_asyncs);
}
};

Expand Down Expand Up @@ -222,93 +224,45 @@ class ThreadToNativeFromVM : public ThreadStateTransition {
};


// Parameter in_flight_mutex_addr is only used by class Mutex to avoid certain deadlock
// scenarios while making transitions that might block for a safepoint or handshake.
// It's the address of a pointer to the mutex we are trying to acquire. This will be used to
// access and release said mutex when transitioning back from blocked to vm (destructor) in
// case we need to stop for a safepoint or handshake.
class ThreadBlockInVM : public ThreadStateTransition {
public:
ThreadBlockInVM(JavaThread *thread)
: ThreadStateTransition(thread) {
// Once we are blocked vm expects stack to be walkable
thread->frame_anchor()->make_walkable(thread);
trans(_thread_in_vm, _thread_blocked);
}
~ThreadBlockInVM() {
trans(_thread_blocked, _thread_in_vm);
// We don't need to clear_walkable because it will happen automagically when we return to java
}
};

// Unlike ThreadBlockInVM, this class is designed to avoid certain deadlock scenarios while making
// transitions inside class Mutex in cases where we need to block for a safepoint or handshake. It
// receives an extra argument compared to ThreadBlockInVM, the address of a pointer to the mutex we
// are trying to acquire. This will be used to access and release the mutex if needed to avoid
// said deadlocks.
// It works like ThreadBlockInVM but differs from it in two ways:
// - When transitioning in (constructor), it checks for safepoints without blocking, i.e., calls
// back if needed to allow a pending safepoint to continue but does not block in it.
// - When transitioning back (destructor), if there is a pending safepoint or handshake it releases
// the mutex that is only partially acquired.
class ThreadBlockInVMWithDeadlockCheck : public ThreadStateTransition {
private:
Mutex** _in_flight_mutex_addr;

void release_mutex() {
assert(_in_flight_mutex_addr != NULL, "_in_flight_mutex_addr should have been set on constructor");
Mutex* in_flight_mutex = *_in_flight_mutex_addr;
if (in_flight_mutex != NULL) {
in_flight_mutex->release_for_safepoint();
*_in_flight_mutex_addr = NULL;
}
}
public:
ThreadBlockInVMWithDeadlockCheck(JavaThread* thread, Mutex** in_flight_mutex_addr)
ThreadBlockInVM(JavaThread* thread, Mutex** in_flight_mutex_addr = NULL)
: ThreadStateTransition(thread), _in_flight_mutex_addr(in_flight_mutex_addr) {
assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
thread->check_possible_safepoint();
// Once we are blocked vm expects stack to be walkable
thread->frame_anchor()->make_walkable(thread);

// All unsafe states are treated the same by the VMThread
// so we can skip the _thread_in_vm_trans state here. Since
// we don't read poll, it's enough to order the stores.
OrderAccess::storestore();

thread->set_thread_state(_thread_blocked);
}
~ThreadBlockInVMWithDeadlockCheck() {
~ThreadBlockInVM() {
assert(_thread->thread_state() == _thread_blocked, "coming from wrong thread state");
// Change to transition state and ensure it is seen by the VM thread.
_thread->set_thread_state_fence((JavaThreadState)(_thread_blocked_trans));
_thread->set_thread_state_fence(_thread_blocked_trans);

if (SafepointMechanism::should_process(_thread)) {
release_mutex();
if (_in_flight_mutex_addr != NULL) {
release_mutex();
}
SafepointMechanism::process_if_requested(_thread);
}

_thread->set_thread_state(_thread_in_vm);
}
};


// This special transition class is only used to prevent asynchronous exceptions
// from being installed on vm exit in situations where we can't tolerate them.
// See bugs: 4324348, 4854693, 4998314, 5040492, 5050705.
class ThreadInVMfromJavaNoAsyncException : public ThreadStateTransition {
public:
ThreadInVMfromJavaNoAsyncException(JavaThread* thread) : ThreadStateTransition(thread) {
trans_from_java(_thread_in_vm);
}
~ThreadInVMfromJavaNoAsyncException() {
if (_thread->stack_overflow_state()->stack_yellow_reserved_zone_disabled()) {
_thread->stack_overflow_state()->enable_stack_yellow_reserved_zone();
void release_mutex() {
Mutex* in_flight_mutex = *_in_flight_mutex_addr;
if (in_flight_mutex != NULL) {
in_flight_mutex->release_for_safepoint();
*_in_flight_mutex_addr = NULL;
}
trans(_thread_in_vm, _thread_in_Java);
// NOTE: We do not check for pending. async. exceptions.
// If we did and moved the pending async exception over into the
// pending exception field, we would need to deopt (currently C2
// only). However, to do so would require that we transition back
// to the _thread_in_vm state. Instead we postpone the handling of
// the async exception.


// Check for pending. suspends only.
if (_thread->has_special_runtime_exit_condition())
_thread->handle_special_runtime_exit_condition(false);
}
};

Expand Down Expand Up @@ -383,7 +337,7 @@ class VMNativeEntryWrapper {

#define JRT_ENTRY_NO_ASYNC(result_type, header) \
result_type header { \
ThreadInVMfromJavaNoAsyncException __tiv(thread); \
ThreadInVMfromJava __tiv(thread, false /* check asyncs */); \
VM_ENTRY_BASE(result_type, header, thread) \
debug_only(VMEntryWrapper __vew;)

Expand All @@ -401,7 +355,7 @@ class VMNativeEntryWrapper {

#define JRT_BLOCK_NO_ASYNC \
{ \
ThreadInVMfromJavaNoAsyncException __tiv(thread); \
ThreadInVMfromJava __tiv(thread, false /* check asyncs */); \
Thread* THREAD = thread; \
debug_only(VMEntryWrapper __vew;)

Expand Down
14 changes: 7 additions & 7 deletions src/hotspot/share/runtime/mutex.cpp
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 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
Expand Down Expand Up @@ -84,12 +84,12 @@ void Mutex::lock_contended(Thread* self) {
// Is it a JavaThread participating in the safepoint protocol.
if (is_active_Java_thread) {
assert(rank() > Mutex::special, "Potential deadlock with special or lesser rank mutex");
{ ThreadBlockInVMWithDeadlockCheck tbivmdc(self->as_Java_thread(), &in_flight_mutex);
in_flight_mutex = this; // save for ~ThreadBlockInVMWithDeadlockCheck
{ ThreadBlockInVM tbivmdc(self->as_Java_thread(), &in_flight_mutex);
in_flight_mutex = this; // save for ~ThreadBlockInVM
_lock.lock();
}
if (in_flight_mutex != NULL) {
// Not unlocked by ~ThreadBlockInVMWithDeadlockCheck
// Not unlocked by ~ThreadBlockInVM
break;
}
} else {
Expand Down Expand Up @@ -236,7 +236,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
Mutex* in_flight_mutex = NULL;

{
ThreadBlockInVMWithDeadlockCheck tbivmdc(self, &in_flight_mutex);
ThreadBlockInVM tbivmdc(self, &in_flight_mutex);
OSThreadWaitState osts(self->osthread(), false /* not Object.wait() */);
if (as_suspend_equivalent) {
self->set_suspend_equivalent();
Expand All @@ -245,7 +245,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
}

wait_status = _lock.wait(timeout);
in_flight_mutex = this; // save for ~ThreadBlockInVMWithDeadlockCheck
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()) {
Expand All @@ -260,7 +260,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
}

if (in_flight_mutex != NULL) {
// Not unlocked by ~ThreadBlockInVMWithDeadlockCheck
// Not unlocked by ~ThreadBlockInVM
assert_owner(NULL);
// Conceptually reestablish ownership of the lock.
set_owner(self);
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/runtime/safepoint.cpp
Expand Up @@ -968,7 +968,7 @@ void ThreadSafepointState::handle_polling_page_exception() {
// If we have a pending async exception deoptimize the frame
// as otherwise we may never deliver it.
if (self->has_async_condition()) {
ThreadInVMfromJavaNoAsyncException __tiv(self);
ThreadInVMfromJava __tiv(self, false /* check asyncs */);
Deoptimization::deoptimize_frame(self, caller_fr.id());
}

Expand Down

1 comment on commit d6b5e18

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.