8283044: Use asynchronous handshakes to deliver asynchronous exceptions

Reviewed-by: dcubed, dholmes, rehn
This commit is contained in:
Patricio Chilano Mateo 2022-04-04 14:00:26 +00:00
parent 9d200d6e7a
commit 4e20a03786
20 changed files with 772 additions and 262 deletions

View File

@ -549,7 +549,9 @@ JNI_END
static void jni_check_async_exceptions(JavaThread *thread) {
assert(thread == Thread::current(), "must be itself");
thread->check_and_handle_async_exceptions();
if (thread->has_async_exception_condition()) {
SafepointMechanism::process_if_requested_with_exit_check(thread, true /* check asyncs */);
}
}
JNI_ENTRY_NO_PRESERVE(jthrowable, jni_ExceptionOccurred(JNIEnv *env))

View File

@ -24,6 +24,8 @@
#include "precompiled.hpp"
#include "jvm_io.h"
#include "classfile/javaClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
@ -78,6 +80,8 @@ class HandshakeOperation : public CHeapObj<mtThread> {
const char* name() { return _handshake_cl->name(); }
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 {
@ -313,7 +317,6 @@ void HandshakeOperation::do_handshake(JavaThread* thread) {
// Only actually execute the operation for non terminated threads.
if (!thread->is_terminated()) {
NoSafepointVerifier nsv;
_handshake_cl->do_thread(thread);
}
@ -426,6 +429,7 @@ HandshakeState::HandshakeState(JavaThread* target) :
_queue(),
_lock(Monitor::nosafepoint, "HandshakeState_lock"),
_active_handshaker(),
_async_exceptions_blocked(false),
_suspended(false),
_async_suspend_handshake(false)
{
@ -443,39 +447,61 @@ bool HandshakeState::operation_pending(HandshakeOperation* op) {
return _queue.contains(mo);
}
static bool no_suspend_filter(HandshakeOperation* op) {
return !op->is_suspend();
// Filters
static bool non_self_executable_filter(HandshakeOperation* op) {
return !op->is_async();
}
static bool no_async_exception_filter(HandshakeOperation* op) {
return !op->is_async_exception();
}
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();
}
HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend) {
HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend, bool check_async_exception) {
assert(_handshakee == Thread::current(), "Must be called by self");
assert(_lock.owned_by_self(), "Lock must be held");
if (allow_suspend) {
assert(allow_suspend || !check_async_exception, "invalid case");
if (!allow_suspend) {
return _queue.peek(no_suspend_no_async_exception_filter);
} else if (check_async_exception && !_async_exceptions_blocked) {
return _queue.peek();
} else {
return _queue.peek(no_suspend_filter);
return _queue.peek(no_async_exception_filter);
}
}
static bool non_self_queue_filter(HandshakeOperation* op) {
return !op->is_async();
bool HandshakeState::has_operation(bool allow_suspend, bool check_async_exception) {
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
return get_op_for_self(allow_suspend, check_async_exception) != NULL;
}
bool HandshakeState::has_async_exception_operation(bool ThreadDeath_only) {
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;
}
}
bool HandshakeState::have_non_self_executable_operation() {
assert(_handshakee != Thread::current(), "Must not be called by self");
assert(_lock.owned_by_self(), "Lock must be held");
return _queue.contains(non_self_queue_filter);
}
bool HandshakeState::has_a_non_suspend_operation() {
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
return _queue.contains(no_suspend_filter);
return _queue.contains(non_self_executable_filter);
}
HandshakeOperation* HandshakeState::get_op() {
assert(_handshakee != Thread::current(), "Must not be called by self");
assert(_lock.owned_by_self(), "Lock must be held");
return _queue.peek(non_self_queue_filter);
return _queue.peek(non_self_executable_filter);
};
void HandshakeState::remove_op(HandshakeOperation* op) {
@ -485,7 +511,7 @@ void HandshakeState::remove_op(HandshakeOperation* op) {
assert(ret == op, "Popped op must match requested op");
};
bool HandshakeState::process_by_self(bool allow_suspend) {
bool HandshakeState::process_by_self(bool allow_suspend, bool check_async_exception) {
assert(Thread::current() == _handshakee, "should call from _handshakee");
assert(!_handshakee->is_terminated(), "should not be a terminated thread");
@ -493,15 +519,12 @@ bool HandshakeState::process_by_self(bool allow_suspend) {
// Threads shouldn't block if they are in the middle of printing, but...
ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id());
// 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;
while (has_operation()) {
// Handshakes cannot safely safepoint. The exceptions to this rule are
// the asynchronous suspension and unsafe access error handshakes.
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
HandshakeOperation* op = get_op_for_self(allow_suspend);
HandshakeOperation* op = get_op_for_self(allow_suspend, check_async_exception);
if (op != NULL) {
assert(op->_target == NULL || op->_target == Thread::current(), "Wrong thread");
bool async = op->is_async();
@ -517,8 +540,8 @@ bool HandshakeState::process_by_self(bool allow_suspend) {
// 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); // acquire, op removed after
remove_op(op);
op->do_handshake(_handshakee);
log_handshake_info(((AsyncHandshakeOperation*)op)->start_time(), op->name(), 1, 0, "asynchronous");
delete op;
return true; // Must check for safepoints
@ -730,3 +753,29 @@ bool HandshakeState::resume() {
_lock.notify();
return true;
}
void HandshakeState::handle_unsafe_access_error() {
if (is_suspended()) {
// A suspend handshake was added to the queue after the
// unsafe access error. Since the suspender has already
// considered this JT as suspended and assumes it won't go
// back to Java until resumed we cannot create the exception
// object yet. Add a new unsafe access error operation to
// the end of the queue and try again in the next attempt.
Handshake::execute(new UnsafeAccessErrorHandshake(), _handshakee);
log_info(handshake)("JavaThread " INTPTR_FORMAT " skipping unsafe access processing due to suspend.", p2i(_handshakee));
return;
}
// Release the handshake lock before constructing the oop to
// avoid deadlocks since that can block. This will allow the
// JavaThread to execute normally as if it was outside a handshake.
// We will reacquire the handshake lock at return from ~MutexUnlocker.
MutexUnlocker ml(&_lock, Mutex::_no_safepoint_check_flag);
// We may be at method entry which requires we save the do-not-unlock flag.
UnlockFlagSaver fs(_handshakee);
Handle h_exception = Exceptions::new_exception(_handshakee, vmSymbols::java_lang_InternalError(), "a fault occurred in an unsafe memory access operation");
if (h_exception()->is_a(vmClasses::InternalError_klass())) {
java_lang_InternalError::set_during_unsafe_access(h_exception());
}
_handshakee->handle_async_exception(h_exception());
}

View File

@ -33,9 +33,11 @@
#include "utilities/filterQueue.hpp"
class HandshakeOperation;
class AsyncHandshakeOperation;
class JavaThread;
class SuspendThreadHandshake;
class ThreadSelfSuspensionHandshake;
class UnsafeAccessErrorHandshake;
class ThreadsListHandle;
// A handshake closure is a callback that is executed for a JavaThread
@ -51,6 +53,8 @@ class HandshakeClosure : public ThreadClosure, public CHeapObj<mtThread> {
const char* name() const { return _name; }
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;
};
@ -87,6 +91,7 @@ class JvmtiRawMonitor;
class HandshakeState {
friend ThreadSelfSuspensionHandshake;
friend SuspendThreadHandshake;
friend UnsafeAccessErrorHandshake;
friend JavaThread;
// This a back reference to the JavaThread,
// the target for all operation in the queue.
@ -104,7 +109,7 @@ class HandshakeState {
bool can_process_handshake();
bool have_non_self_executable_operation();
HandshakeOperation* get_op_for_self(bool allow_suspend);
HandshakeOperation* get_op_for_self(bool allow_suspend, bool check_async_exception);
HandshakeOperation* get_op();
void remove_op(HandshakeOperation* op);
@ -124,17 +129,16 @@ class HandshakeState {
void add_operation(HandshakeOperation* op);
bool has_operation() {
return !_queue.is_empty();
}
bool has_a_non_suspend_operation();
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 operation_pending(HandshakeOperation* op);
// If the method returns true we need to check for a possible safepoint.
// This is due to a suspension handshake which put the JavaThread in blocked
// state so a safepoint may be in-progress.
bool process_by_self(bool allow_suspend);
bool process_by_self(bool allow_suspend, bool check_async_exception);
enum ProcessResult {
_no_operation = 0,
@ -148,6 +152,14 @@ class HandshakeState {
Thread* active_handshaker() const { return Atomic::load(&_active_handshaker); }
// Support for asynchronous exceptions
private:
bool _async_exceptions_blocked;
bool async_exceptions_blocked() { return _async_exceptions_blocked; }
void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; }
void handle_unsafe_access_error();
// Suspend/resume support
private:
// This flag is true when the thread owning this

View File

@ -210,7 +210,7 @@ class ThreadBlockInVMPreprocess : public ThreadStateTransition {
if (SafepointMechanism::should_process(_thread, _allow_suspend)) {
_pr(_thread);
SafepointMechanism::process_if_requested(_thread, _allow_suspend);
SafepointMechanism::process_if_requested(_thread, _allow_suspend, false /* check_async_exception */);
}
}
};

View File

@ -1897,7 +1897,10 @@ int ObjectMonitor::TrySpin(JavaThread* current) {
// This is in keeping with the "no loitering in runtime" rule.
// We periodically check to see if there's a safepoint pending.
if ((ctr & 0xFF) == 0) {
if (SafepointMechanism::should_process(current)) {
// Can't call SafepointMechanism::should_process() since that
// might update the poll values and we could be in a thread_blocked
// state here which is not allowed so just check the poll.
if (SafepointMechanism::local_poll_armed(current)) {
goto Abort; // abrupt spin egress
}
SpinPause();

View File

@ -963,33 +963,34 @@ void ThreadSafepointState::handle_polling_page_exception() {
set_at_poll_safepoint(true);
// Process pending operation
// We never deliver an async exception at a polling point as the
// compiler may not have an exception handler for it. The polling
// code will notice the pending async exception, deoptimize and
// the exception will be delivered. (Polling at a return point
// is ok though). Sure is a lot of bother for a deprecated feature...
// compiler may not have an exception handler for it (polling at
// a return point is ok though). We will check for a pending async
// exception below and deoptimize if needed. We also cannot deoptimize
// and still install the exception here because live registers needed
// during deoptimization are clobbered by the exception path. The
// exception will just be delivered once we get into the interpreter.
SafepointMechanism::process_if_requested_with_exit_check(self, false /* check asyncs */);
set_at_poll_safepoint(false);
// If we have a pending async exception deoptimize the frame
// as otherwise we may never deliver it.
if (self->has_async_exception_condition()) {
Deoptimization::deoptimize_frame(self, caller_fr.id());
log_info(exceptions)("deferred async exception at compiled safepoint");
}
// If an exception has been installed we must check for a pending deoptimization
// Deoptimize frame if exception has been thrown.
// If an exception has been installed we must verify that the top frame wasn't deoptimized.
if (self->has_pending_exception() ) {
RegisterMap map(self, true, false);
frame caller_fr = stub_fr.sender(&map);
if (caller_fr.is_deoptimized_frame()) {
// The exception patch will destroy registers that are still
// live and will be needed during deoptimization. Defer the
// Async exception should have deferred the exception until the
// next safepoint which will be detected when we get into
// the interpreter so if we have an exception now things
// are messed up.
// The exception path will destroy registers that are still
// live and will be needed during deoptimization, so if we
// have an exception now things are messed up. We only check
// at this scope because for a poll return it is ok to deoptimize
// while having a pending exception since the call we are returning
// from already collides with exception handling registers and
// so there is no issue (the exception handling path kills call
// result registers but this is ok since the exception kills
// the result anyway).
fatal("Exception installed and deoptimization is pending");
}
}

View File

@ -111,7 +111,7 @@ void SafepointMechanism::update_poll_values(JavaThread* thread) {
}
}
void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) {
void SafepointMechanism::process(JavaThread *thread, bool allow_suspend, bool check_async_exception) {
// Read global poll and has_handshake after local poll
OrderAccess::loadload();
@ -135,7 +135,7 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) {
// 3) Before the handshake code is run
StackWatermarkSet::on_safepoint(thread);
need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend);
need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend, check_async_exception);
} while (need_rechecking);
update_poll_values(thread);

View File

@ -49,7 +49,7 @@ class SafepointMechanism : public AllStatic {
static inline bool global_poll();
static void process(JavaThread *thread, bool allow_suspend);
static void process(JavaThread *thread, bool allow_suspend, bool check_async_exception);
static void default_initialize();
@ -80,8 +80,8 @@ class SafepointMechanism : public AllStatic {
static inline bool should_process(JavaThread* thread, bool allow_suspend = true);
// Processes a pending requested operation.
static inline void process_if_requested(JavaThread* thread, bool allow_suspend = true);
static inline void process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs);
static inline void process_if_requested(JavaThread* thread, bool allow_suspend, bool check_async_exception);
static inline void process_if_requested_with_exit_check(JavaThread* thread, bool check_async_exception);
// Compute what the poll values should be and install them.
static void update_poll_values(JavaThread* thread);

View File

@ -66,19 +66,17 @@ bool SafepointMechanism::global_poll() {
bool SafepointMechanism::should_process(JavaThread* thread, bool allow_suspend) {
if (!local_poll_armed(thread)) {
return false;
} else if (allow_suspend) {
return true;
}
// We are armed but we should ignore suspend operations.
if (global_poll() || // Safepoint
thread->handshake_state()->has_a_non_suspend_operation() || // Non-suspend handshake
thread->handshake_state()->has_operation(allow_suspend, false /* check_async_exception */) || // Handshake
!StackWatermarkSet::processing_started(thread)) { // StackWatermark processing is not started
return true;
}
// It has boiled down to two possibilities:
// 1: We have nothing to process, this just a disarm poll.
// 2: We have a suspend handshake, which cannot be processed.
// 2: We have a suspend or async exception handshake, which cannot be processed.
// We update the poll value in case of a disarm, to reduce false positives.
update_poll_values(thread);
@ -88,19 +86,19 @@ bool SafepointMechanism::should_process(JavaThread* thread, bool allow_suspend)
return false;
}
void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend) {
void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend, bool check_async_exception) {
// Check NoSafepointVerifier. This also clears unhandled oops if CheckUnhandledOops is used.
thread->check_possible_safepoint();
if (local_poll_armed(thread)) {
process(thread, allow_suspend);
process(thread, allow_suspend, check_async_exception);
}
}
void SafepointMechanism::process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs) {
process_if_requested(thread);
void SafepointMechanism::process_if_requested_with_exit_check(JavaThread* thread, bool check_async_exception) {
process_if_requested(thread, true /* allow_suspend */, check_async_exception);
if (thread->has_special_runtime_exit_condition()) {
thread->handle_special_runtime_exit_condition(check_asyncs);
thread->handle_special_runtime_exit_condition();
}
}

View File

@ -266,7 +266,7 @@ void NMethodSweeper::force_sweep() {
*/
void NMethodSweeper::handle_safepoint_request() {
JavaThread* thread = JavaThread::current();
if (SafepointMechanism::should_process(thread)) {
if (SafepointMechanism::local_poll_armed(thread)) {
if (PrintMethodFlushing && Verbose) {
tty->print_cr("### Sweep at %d out of %d, yielding to safepoint", _seen, CodeCache::nmethod_count());
}

View File

@ -1014,10 +1014,6 @@ JavaThread::JavaThread() :
_monitor_chunks(nullptr),
_suspend_flags(0),
_pending_async_exception(nullptr),
#ifdef ASSERT
_is_unsafe_access_error(false),
#endif
_thread_state(_thread_new),
_saved_exception_pc(nullptr),
@ -1585,159 +1581,106 @@ void JavaThread::remove_monitor_chunk(MonitorChunk* chunk) {
}
}
// Asynchronous exceptions support
//
void JavaThread::check_and_handle_async_exceptions() {
if (has_last_Java_frame() && has_async_exception_condition()) {
// If we are at a polling page safepoint (not a poll return)
// then we must defer async exception because live registers
// will be clobbered by the exception path. Poll return is
// ok because the call we are returning from already collides
// with exception handling registers and so there is no issue.
// (The exception handling path kills call result registers but
// this is ok since the exception kills the result anyway).
if (is_at_poll_safepoint()) {
// if the code we are returning to has deoptimized we must defer
// the exception otherwise live registers get clobbered on the
// exception path before deoptimization is able to retrieve them.
//
RegisterMap map(this, false);
frame caller_fr = last_frame().sender(&map);
assert(caller_fr.is_compiled_frame(), "what?");
if (caller_fr.is_deoptimized_frame()) {
log_info(exceptions)("deferred async exception at compiled safepoint");
return;
}
}
}
if (!clear_async_exception_condition()) {
if ((_suspend_flags & _async_delivery_disabled) != 0) {
log_info(exceptions)("Async exception delivery is disabled");
}
return;
}
if (_pending_async_exception != NULL) {
// 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(_pending_async_exception, __FILE__, __LINE__);
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", _pending_async_exception->klass()->external_name());
}
}
// Always null out the _pending_async_exception oop here since the async condition was
// already cleared above and thus considered handled.
_pending_async_exception = NULL;
} else {
assert(_is_unsafe_access_error, "must be");
DEBUG_ONLY(_is_unsafe_access_error = false);
// We may be at method entry which requires we save the do-not-unlock flag.
UnlockFlagSaver fs(this);
Exceptions::throw_unsafe_access_internal_error(this, __FILE__, __LINE__, "a fault occurred in an unsafe memory access operation");
// We might have blocked in a ThreadBlockInVM wrapper in the call above so make sure we process pending
// suspend requests and object reallocation operations if any since we might be going to Java after this.
SafepointMechanism::process_if_requested_with_exit_check(this, true /* check asyncs */);
}
}
void JavaThread::handle_special_runtime_exit_condition(bool check_asyncs) {
void JavaThread::handle_special_runtime_exit_condition() {
if (is_obj_deopt_suspend()) {
frame_anchor()->make_walkable(this);
wait_for_object_deoptimization();
}
// We might be here for reasons in addition to the self-suspend request
// so check for other async requests.
if (check_asyncs) {
check_and_handle_async_exceptions();
}
JFR_ONLY(SUSPEND_THREAD_CONDITIONAL(this);)
}
class InstallAsyncExceptionClosure : public HandshakeClosure {
Handle _throwable; // The Throwable thrown at the target Thread
public:
InstallAsyncExceptionClosure(Handle throwable) : HandshakeClosure("InstallAsyncException"), _throwable(throwable) {}
void do_thread(Thread* thr) {
JavaThread* target = JavaThread::cast(thr);
// Note that this now allows multiple ThreadDeath exceptions to be
// thrown at a thread.
// The target thread has run and has not exited yet.
target->send_thread_stop(_throwable());
}
};
// Asynchronous exceptions support
//
void JavaThread::handle_async_exception(oop java_throwable) {
assert(java_throwable != NULL, "should have an _async_exception to throw");
assert(!is_at_poll_safepoint(), "should have never called this method");
void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) {
Handle throwable(Thread::current(), java_throwable);
InstallAsyncExceptionClosure vm_stop(throwable);
Handshake::execute(&vm_stop, target);
}
void JavaThread::send_thread_stop(oop java_throwable) {
ResourceMark rm;
assert(is_handshake_safe_for(Thread::current()),
"should be self or handshakee");
// Do not throw asynchronous exceptions against the compiler thread
// (the compiler thread should not be a Java thread -- fix in 1.4.2)
if (!can_call_java()) return;
{
// Actually throw the Throwable against the target Thread - however
// only if there is no thread death exception installed already.
if (_pending_async_exception == NULL || !_pending_async_exception->is_a(vmClasses::ThreadDeath_klass())) {
if (has_last_Java_frame()) {
frame f = last_frame();
if (f.is_runtime_frame()) {
// If the topmost frame is a runtime stub, then we are calling into
// OptoRuntime from compiled code. Some runtime stubs (new, monitor_exit..)
// must deoptimize the caller before continuing, as the compiled exception handler table
// may not be valid
if (has_last_Java_frame()) {
frame f = last_frame();
if (f.is_runtime_frame() || f.is_safepoint_blob_frame()) {
RegisterMap reg_map(this, false);
frame compiled_frame = f.sender(&reg_map);
if (!StressCompiledExceptionHandlers && compiled_frame.can_be_deoptimized()) {
Deoptimization::deoptimize(this, compiled_frame);
}
}
// must deoptimize the caller before continuing, as the compiled exception
// handler table may not be valid.
RegisterMap reg_map(this, false);
frame compiled_frame = f.sender(&reg_map);
if (!StressCompiledExceptionHandlers && compiled_frame.can_be_deoptimized()) {
Deoptimization::deoptimize(this, compiled_frame);
}
// Set async. pending exception in thread.
set_pending_async_exception(java_throwable);
if (log_is_enabled(Info, exceptions)) {
ResourceMark rm;
log_info(exceptions)("Pending Async. exception installed of type: %s",
InstanceKlass::cast(_pending_async_exception->klass())->external_name());
}
// for AbortVMOnException flag
Exceptions::debug_check_abort(_pending_async_exception->klass()->external_name());
}
}
// 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__);
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());
}
}
}
void JavaThread::install_async_exception(AsyncExceptionHandshake* aeh) {
// Do not throw asynchronous exceptions against the compiler thread.
if (!can_call_java()) {
delete 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
ResourceMark rm;
if (log_is_enabled(Info, exceptions)) {
log_info(exceptions)("Pending Async. exception installed of type: %s",
InstanceKlass::cast(exception->klass())->external_name());
}
// for AbortVMOnException flag
Exceptions::debug_check_abort(exception->klass()->external_name());
// Interrupt thread so it will wake up from a potential wait()/sleep()/park()
java_lang_Thread::set_interrupted(threadObj(), true);
this->interrupt();
}
class InstallAsyncExceptionHandshake : public HandshakeClosure {
AsyncExceptionHandshake* _aeh;
public:
InstallAsyncExceptionHandshake(AsyncExceptionHandshake* aeh) :
HandshakeClosure("InstallAsyncException"), _aeh(aeh) {}
void do_thread(Thread* thr) {
JavaThread* target = JavaThread::cast(thr);
target->install_async_exception(_aeh);
}
};
void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) {
OopHandle e(Universe::vm_global(), java_throwable);
InstallAsyncExceptionHandshake iaeh(new AsyncExceptionHandshake(e));
Handshake::execute(&iaeh, target);
}
// External suspension mechanism.
//
@ -1966,7 +1909,6 @@ void JavaThread::oops_do_no_frames(OopClosure* f, CodeBlobClosure* cf) {
// around using this function
f->do_oop((oop*) &_vm_result);
f->do_oop((oop*) &_exception_oop);
f->do_oop((oop*) &_pending_async_exception);
#if INCLUDE_JVMCI
f->do_oop((oop*) &_jvmci_reserved_oop0);
#endif

View File

@ -68,6 +68,7 @@ class OSThread;
class ThreadStatistics;
class ConcurrentLocksDump;
class MonitorInfo;
class AsyncExceptionHandshake;
class vframeArray;
class vframe;
@ -785,14 +786,11 @@ class JavaThread: public Thread {
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
_has_async_exception = 0x00000001U, // there is a pending async exception
_async_delivery_disabled = 0x00000002U, // async exception delivery is disabled
_trace_flag = 0x00000004U, // call tracing backend
_obj_deopt = 0x00000008U // suspend for object reallocation and relocking for JVMTI agent
_trace_flag = 0x00000004U, // call tracing backend
_obj_deopt = 0x00000008U // suspend for object reallocation and relocking for JVMTI agent
};
// various suspension related flags - atomically updated
// overloaded with async exceptions so that we do a single check when transitioning from native->Java
volatile uint32_t _suspend_flags;
inline void set_suspend_flag(SuspendFlags f);
@ -806,24 +804,18 @@ class JavaThread: public Thread {
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
bool is_obj_deopt_suspend() { return (_suspend_flags & _obj_deopt) != 0; }
// Asynchronous exceptions support
// Asynchronous exception support
private:
oop _pending_async_exception;
#ifdef ASSERT
bool _is_unsafe_access_error;
#endif
friend class InstallAsyncExceptionHandshake;
friend class AsyncExceptionHandshake;
friend class HandshakeState;
inline bool clear_async_exception_condition();
void install_async_exception(AsyncExceptionHandshake* aec = NULL);
void handle_async_exception(oop java_throwable);
public:
bool has_async_exception_condition() {
return (_suspend_flags & _has_async_exception) != 0 &&
(_suspend_flags & _async_delivery_disabled) == 0;
}
inline void set_pending_async_exception(oop e);
bool has_async_exception_condition(bool ThreadDeath_only = false);
inline void set_pending_unsafe_access_error();
static void send_async_exception(JavaThread* jt, oop java_throwable);
void send_thread_stop(oop throwable);
void check_and_handle_async_exceptions();
class NoAsyncExceptionDeliveryMark : public StackObj {
friend JavaThread;
@ -1169,13 +1161,10 @@ class JavaThread: public Thread {
// current thread, i.e. reverts optimizations based on escape analysis.
void wait_for_object_deoptimization();
// these next two are also used for self-suspension and async exception support
void handle_special_runtime_exit_condition(bool check_asyncs = true);
// Return true if JavaThread has an asynchronous condition or
// if external suspension is requested.
// Support for object deoptimization and JFR suspension
void handle_special_runtime_exit_condition();
bool has_special_runtime_exit_condition() {
return (_suspend_flags & (_has_async_exception | _obj_deopt JFR_ONLY(| _trace_flag))) != 0;
return (_suspend_flags & (_obj_deopt JFR_ONLY(| _trace_flag))) != 0;
}
// Fast-locking support

View File

@ -28,7 +28,11 @@
#include "runtime/thread.hpp"
#include "classfile/vmClasses.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/oopHandle.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/nonJavaThread.hpp"
#include "runtime/orderAccess.hpp"
@ -122,31 +126,62 @@ inline void JavaThread::clear_obj_deopt_flag() {
clear_suspend_flag(_obj_deopt);
}
inline bool JavaThread::clear_async_exception_condition() {
bool ret = has_async_exception_condition();
if (ret) {
clear_suspend_flag(_has_async_exception);
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());
}
return ret;
}
inline void JavaThread::set_pending_async_exception(oop e) {
_pending_async_exception = e;
set_suspend_flag(_has_async_exception);
}
~AsyncExceptionHandshake() {
assert(!_exception.is_empty(), "invariant");
_exception.release(Universe::vm_global());
}
void do_thread(Thread* thr) {
JavaThread* self = JavaThread::cast(thr);
assert(self == JavaThread::current(), "must be");
self->handle_async_exception(exception());
}
oop exception() {
assert(!_exception.is_empty(), "invariant");
return _exception.resolve();
}
bool is_async_exception() { return true; }
bool is_ThreadDeath() { return _is_ThreadDeath; }
};
class UnsafeAccessErrorHandshake : public AsyncHandshakeClosure {
public:
UnsafeAccessErrorHandshake() : AsyncHandshakeClosure("UnsafeAccessErrorHandshake") {}
void do_thread(Thread* thr) {
JavaThread* self = JavaThread::cast(thr);
assert(self == JavaThread::current(), "must be");
self->handshake_state()->handle_unsafe_access_error();
}
bool is_async_exception() { return true; }
};
inline void JavaThread::set_pending_unsafe_access_error() {
set_suspend_flag(_has_async_exception);
DEBUG_ONLY(_is_unsafe_access_error = true);
if (!has_async_exception_condition()) {
Handshake::execute(new UnsafeAccessErrorHandshake(), this);
}
}
inline bool JavaThread::has_async_exception_condition(bool ThreadDeath_only) {
return handshake_state()->has_async_exception_operation(ThreadDeath_only);
}
inline JavaThread::NoAsyncExceptionDeliveryMark::NoAsyncExceptionDeliveryMark(JavaThread *t) : _target(t) {
assert((_target->_suspend_flags & _async_delivery_disabled) == 0, "Nesting is not supported");
_target->set_suspend_flag(_async_delivery_disabled);
assert(!_target->handshake_state()->async_exceptions_blocked(), "Nesting is not supported");
_target->handshake_state()->set_async_exceptions_blocked(true);
}
inline JavaThread::NoAsyncExceptionDeliveryMark::~NoAsyncExceptionDeliveryMark() {
_target->clear_suspend_flag(_async_delivery_disabled);
_target->handshake_state()->set_async_exceptions_blocked(false);
}
inline JavaThreadState JavaThread::thread_state() const {

View File

@ -716,7 +716,6 @@
nonstatic_field(JavaThread, _current_pending_monitor_is_from_java, bool) \
volatile_nonstatic_field(JavaThread, _current_waiting_monitor, ObjectMonitor*) \
volatile_nonstatic_field(JavaThread, _suspend_flags, uint32_t) \
nonstatic_field(JavaThread, _pending_async_exception, oop) \
volatile_nonstatic_field(JavaThread, _exception_oop, oop) \
volatile_nonstatic_field(JavaThread, _exception_pc, address) \
volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \
@ -2121,12 +2120,6 @@
declare_constant(JVM_CONSTANT_DynamicInError) \
declare_constant(JVM_CONSTANT_InternalMax) \
\
/*****************************/ \
/* Thread::SuspendFlags enum */ \
/*****************************/ \
\
declare_constant(JavaThread::_has_async_exception) \
\
/*******************/ \
/* JavaThreadState */ \
/*******************/ \

View File

@ -250,12 +250,6 @@ void Exceptions::throw_stack_overflow_exception(JavaThread* THREAD, const char*
_throw(THREAD, file, line, exception);
}
void Exceptions::throw_unsafe_access_internal_error(JavaThread* thread, const char* file, int line, const char* message) {
Handle h_exception = new_exception(thread, vmSymbols::java_lang_InternalError(), message);
java_lang_InternalError::set_during_unsafe_access(h_exception());
_throw(thread, file, line, h_exception, message);
}
void Exceptions::fthrow(JavaThread* thread, const char* file, int line, Symbol* h_name, const char* format, ...) {
const int max_msg_size = 1024;
va_list ap;

View File

@ -173,8 +173,6 @@ class Exceptions {
static void throw_stack_overflow_exception(JavaThread* thread, const char* file, int line, const methodHandle& method);
static void throw_unsafe_access_internal_error(JavaThread* thread, const char* file, int line, const char* message);
static void wrap_dynamic_exception(bool is_indy, JavaThread* thread);
// Exception counting for error files of interesting exceptions that may have

View File

@ -34,8 +34,6 @@ public class Thread extends VMObject {
private static long tlabFieldOffset;
private static CIntegerField suspendFlagsField;
// Thread::SuspendFlags enum constants
private static int HAS_ASYNC_EXCEPTION;
private static AddressField currentPendingMonitorField;
private static AddressField currentWaitingMonitorField;
@ -55,7 +53,6 @@ public class Thread extends VMObject {
Type typeJavaThread = db.lookupType("JavaThread");
suspendFlagsField = typeJavaThread.getCIntegerField("_suspend_flags");
HAS_ASYNC_EXCEPTION = db.lookupIntConstant("JavaThread::_has_async_exception").intValue();
tlabFieldOffset = typeThread.getField("_tlab").getOffset();
currentPendingMonitorField = typeJavaThread.getAddressField("_current_pending_monitor");
@ -71,10 +68,6 @@ public class Thread extends VMObject {
return (int) suspendFlagsField.getValue(addr);
}
public boolean hasAsyncException() {
return (suspendFlags() & HAS_ASYNC_EXCEPTION) != 0;
}
public ThreadLocalAllocBuffer tlab() {
return new ThreadLocalAllocBuffer(addr.addOffsetTo(tlabFieldOffset));
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8283044
* @summary Stress delivery of asynchronous exceptions while target is at monitorenter
* @build AsyncExceptionOnMonitorEnter
* @run main/othervm AsyncExceptionOnMonitorEnter 0
* @run main/othervm/native -agentlib:AsyncExceptionOnMonitorEnter AsyncExceptionOnMonitorEnter 1
*/
import java.util.concurrent.Semaphore;
public class AsyncExceptionOnMonitorEnter extends Thread {
private final static int DEF_TIME_MAX = 30; // default max # secs to test
private final static String PROG_NAME = "AsyncExceptionOnMonitorEnter";
private static int TEST_MODE = 0;
public static native int createRawMonitor();
public static native int enterRawMonitor();
public static native int exitRawMonitor();
public static native void destroyRawMonitor();
private static Object o1 = new Object();
private static boolean firstWorker = true;
private static Semaphore sem = new Semaphore(0);
@Override
public void run() {
if (TEST_MODE == 0) {
testWithJavaMonitor();
} else {
testWithJVMTIRawMonitor();
}
}
public void testWithJavaMonitor() {
try {
synchronized (o1) {
if (firstWorker) {
firstWorker = false;
sem.release();
}
Thread.sleep(1000);
}
} catch (ThreadDeath td) {
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
}
}
public void testWithJVMTIRawMonitor() {
boolean savedFirst = false;
try {
int retCode = enterRawMonitor();
if (retCode != 0 && firstWorker) {
throw new RuntimeException("error in JVMTI RawMonitorEnter: retCode=" + retCode);
}
if (firstWorker) {
firstWorker = false;
savedFirst = true;
sem.release();
}
Thread.sleep(1000);
retCode = exitRawMonitor();
if (retCode != 0 && savedFirst) {
throw new RuntimeException("error in JVMTI RawMonitorExit: retCode=" + retCode);
}
} catch (ThreadDeath td) {
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
}
}
public static void main(String[] args) {
int timeMax = DEF_TIME_MAX;
try {
if (args.length == 1) {
TEST_MODE = Integer.parseUnsignedInt(args[0]);
} else if (args.length == 2) {
TEST_MODE = Integer.parseUnsignedInt(args[0]);
timeMax = Integer.parseUnsignedInt(args[1]);
}
if (TEST_MODE != 0 && TEST_MODE != 1) {
System.err.println("'" + TEST_MODE + "': invalid mode");
usage();
}
} catch (NumberFormatException nfe) {
System.err.println("'" + args[0] + "': invalid value.");
usage();
}
System.out.println("About to execute for " + timeMax + " seconds.");
long count = 0;
long start_time = System.currentTimeMillis();
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
count++;
if (TEST_MODE == 1) {
// Create JVMTI raw monitor that will be used
int retCode = createRawMonitor();
if (retCode != 0) {
throw new RuntimeException("error in JVMTI CreateRawMonitor: retCode=" + retCode);
}
}
AsyncExceptionOnMonitorEnter worker1 = new AsyncExceptionOnMonitorEnter();
AsyncExceptionOnMonitorEnter worker2 = new AsyncExceptionOnMonitorEnter();
try {
// Start firstWorker worker and wait until monitor is acquired
firstWorker = true;
worker1.start();
sem.acquire();
// Start second worker and allow some time for target to block on monitorenter
// before executing Thread.stop()
worker2.start();
Thread.sleep(300);
while (true) {
worker2.stop();
if (TEST_MODE != 1) {
// Don't stop() worker1 with JVMTI raw monitors since if the monitor is
// not released worker2 will deadlock on enter
worker1.stop();
}
if (!worker1.isAlive() && !worker2.isAlive()) {
// Done with Thread.stop() calls since
// threads are not alive.
break;
}
}
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
} catch (NoClassDefFoundError ncdfe) {
// Ignore because we're testing Thread.stop() which can
// cause it. Yes, a NoClassDefFoundError that happens
// in a worker thread can subsequently be seen in the
// main thread.
}
try {
worker1.join();
worker2.join();
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
}
if (TEST_MODE == 1) {
// Destroy JVMTI raw monitor used
destroyRawMonitor();
}
}
System.out.println("Executed " + count + " loops in " + timeMax +
" seconds.");
String cmd = System.getProperty("sun.java.command");
if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
// Exit with success in a non-JavaTest environment:
System.exit(0);
}
}
public static void usage() {
System.err.println("Usage: " + PROG_NAME + " [mode [time_max]]");
System.err.println("where:");
System.err.println(" mode 0: Test with Java monitors (default); 1: Test with JVMTI raw monitors");
System.err.println(" time_max max looping time in seconds");
System.err.println(" (default is " + DEF_TIME_MAX +
" seconds)");
System.exit(1);
}
}

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8283044
* @summary Stress delivery of asynchronous exceptions.
* @library /test/lib /test/hotspot/jtreg
* @build AsyncExceptionTest
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI AsyncExceptionTest
*/
import compiler.testlibrary.CompilerUtils;
import compiler.whitebox.CompilerWhiteBoxTest;
import java.util.concurrent.CountDownLatch;
import sun.hotspot.WhiteBox;
public class AsyncExceptionTest extends Thread {
private final static int DEF_TIME_MAX = 30; // default max # secs to test
private final static String PROG_NAME = "AsyncExceptionTest";
public static final WhiteBox WB = WhiteBox.getWhiteBox();
public CountDownLatch exitSyncObj = new CountDownLatch(1);
public CountDownLatch startSyncObj = new CountDownLatch(1);
private boolean realRun;
private boolean firstEntry = true;
private boolean receivedThreadDeathinInternal1 = false;
private boolean receivedThreadDeathinInternal2 = false;
public void setDontInline(String method) {
java.lang.reflect.Method m;
try {
m = AsyncExceptionTest.class.getMethod(method);
} catch(NoSuchMethodException e) {
throw new RuntimeException("Unexpected: " + e);
}
WB.testSetDontInlineMethod(m, true);
}
public void checkCompLevel(String method) {
int highestLevel = CompilerUtils.getMaxCompilationLevel();
java.lang.reflect.Method m;
try {
m = AsyncExceptionTest.class.getMethod(method);
} catch(NoSuchMethodException e) {
throw new RuntimeException("Unexpected: " + e);
}
int compLevel = WB.getMethodCompilationLevel(m);
while (compLevel < (highestLevel - 1)) {
try {
Thread.sleep(200);
} catch (InterruptedException e) { /* ignored */ }
compLevel = WB.getMethodCompilationLevel(m);
}
}
@Override
public void run() {
try {
setDontInline("internalRun1");
setDontInline("internalRun2");
int callCount = CompilerWhiteBoxTest.THRESHOLD;
while (callCount-- > 0) {
receivedThreadDeathinInternal2 = false;
realRun = false;
internalRun1();
}
checkCompLevel("internalRun1");
checkCompLevel("internalRun2");
receivedThreadDeathinInternal2 = false;
realRun = true;
internalRun1();
} catch (ThreadDeath td) {
throw new RuntimeException("Catched ThreadDeath in run() instead of internalRun2() or internalRun1(). receivedThreadDeathinInternal1=" + receivedThreadDeathinInternal1 + "; receivedThreadDeathinInternal2=" + receivedThreadDeathinInternal2);
} catch (NoClassDefFoundError ncdfe) {
// ignore because we're testing Thread.stop() which can cause it
}
if (receivedThreadDeathinInternal2 == false && receivedThreadDeathinInternal1 == false) {
throw new RuntimeException("Didn't catched ThreadDeath in internalRun2() nor in internalRun1(). receivedThreadDeathinInternal1=" + receivedThreadDeathinInternal1 + "; receivedThreadDeathinInternal2=" + receivedThreadDeathinInternal2);
}
exitSyncObj.countDown();
}
public void internalRun1() {
long start_time = System.currentTimeMillis();
try {
while (!receivedThreadDeathinInternal2) {
internalRun2();
}
} catch (ThreadDeath e) {
receivedThreadDeathinInternal1 = true;
}
}
public void internalRun2() {
try {
Integer myLocalCount = 1;
Integer myLocalCount2 = 1;
if (realRun && firstEntry) {
// Tell main thread we have started.
startSyncObj.countDown();
firstEntry = false;
}
while(myLocalCount > 0) {
if (!realRun) {
receivedThreadDeathinInternal2 = true;
break;
}
myLocalCount2 = (myLocalCount % 3) / 2;
myLocalCount -= 1;
}
} catch (ThreadDeath e) {
receivedThreadDeathinInternal2 = true;
}
}
public static void main(String[] args) {
int timeMax = 0;
if (args.length == 0) {
timeMax = DEF_TIME_MAX;
} else {
try {
timeMax = Integer.parseUnsignedInt(args[0]);
} catch (NumberFormatException nfe) {
System.err.println("'" + args[0] + "': invalid timeMax value.");
usage();
}
}
System.out.println("About to execute for " + timeMax + " seconds.");
long count = 0;
long start_time = System.currentTimeMillis();
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
count++;
AsyncExceptionTest thread = new AsyncExceptionTest();
thread.start();
try {
// Wait for the worker thread to get going.
thread.startSyncObj.await();
while (true) {
// Send async exception and wait until it is thrown
thread.stop();
thread.exitSyncObj.await();
Thread.sleep(100);
if (!thread.isAlive()) {
// Done with Thread.stop() calls since
// thread is not alive.
break;
}
}
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
} catch (NoClassDefFoundError ncdfe) {
// Ignore because we're testing Thread.stop() which can
// cause it. Yes, a NoClassDefFoundError that happens
// in a worker thread can subsequently be seen in the
// main thread.
}
try {
thread.join();
} catch (InterruptedException e) {
throw new Error("Unexpected: " + e);
}
}
System.out.println("Executed " + count + " loops in " + timeMax +
" seconds.");
String cmd = System.getProperty("sun.java.command");
if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
// Exit with success in a non-JavaTest environment:
System.exit(0);
}
}
public static void usage() {
System.err.println("Usage: " + PROG_NAME + " [time_max]");
System.err.println("where:");
System.err.println(" time_max max looping time in seconds");
System.err.println(" (default is " + DEF_TIME_MAX +
" seconds)");
System.exit(1);
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <string.h>
#include "jvmti.h"
extern "C" {
static jvmtiEnv* jvmti = NULL;
static jrawMonitorID monitor;
JNIEXPORT jint JNICALL
Java_AsyncExceptionOnMonitorEnter_createRawMonitor(JNIEnv *jni, jclass cls) {
jvmtiError err;
char name[32];
sprintf(name, "MyRawMonitor");
err = jvmti->CreateRawMonitor(name, &monitor);
if (err != JVMTI_ERROR_NONE) {
printf("CreateRawMonitor unexpected error: (%d)\n", err);
}
return err;
}
JNIEXPORT jint JNICALL
Java_AsyncExceptionOnMonitorEnter_enterRawMonitor(JNIEnv *jni, jclass cls) {
jvmtiError err;
err = jvmti->RawMonitorEnter(monitor);
if (err != JVMTI_ERROR_NONE) {
printf("RawMonitorEnter unexpected error: (%d)\n", err);
}
return err;
}
JNIEXPORT jint JNICALL
Java_AsyncExceptionOnMonitorEnter_exitRawMonitor(JNIEnv *jni, jclass cls) {
jvmtiError err;
err = jvmti->RawMonitorExit(monitor);
if (err != JVMTI_ERROR_NONE) {
printf("RawMonitorExit unexpected error: (%d)\n", err);
}
return err;
}
JNIEXPORT void JNICALL
Java_AsyncExceptionOnMonitorEnter_destroyRawMonitor(JNIEnv *jni, jclass cls) {
jvmtiError err;
err = jvmti->DestroyRawMonitor(monitor);
// Almost always worker2 will be stopped before being able to release the
// JVMTI monitor so just ignore those errors.
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_NOT_MONITOR_OWNER) {
printf("DestroyRawMonitor unexpected error: (%d)\n", err);
}
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
// create JVMTI environment
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
return JNI_ERR;
}
return JNI_OK;
}
}