mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-08 12:39:29 +00:00
8366659: ObjectMonitor::wait() liveness problem with a suspension request
Co-authored-by: Patricio Chilano Mateo <pchilanomate@openjdk.org> Co-authored-by: Daniel D. Daugherty <dcubed@openjdk.org> Reviewed-by: dcubed, sspitsyn, dholmes, pchilanomate
This commit is contained in:
parent
766e03b151
commit
1f3fd3da1d
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -339,15 +339,6 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) {
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) {
|
||||
if (current->is_suspended()) {
|
||||
if (_om->has_successor(current)) {
|
||||
_om->clear_successor();
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define assert_mark_word_consistency() \
|
||||
assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \
|
||||
"object mark must match encoded this: mark=" INTPTR_FORMAT \
|
||||
@ -500,7 +491,7 @@ bool ObjectMonitor::spin_enter(JavaThread* current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectMonitor::enter(JavaThread* current) {
|
||||
bool ObjectMonitor::enter(JavaThread* current, bool post_jvmti_events) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
|
||||
if (spin_enter(current)) {
|
||||
@ -521,15 +512,15 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
||||
}
|
||||
|
||||
// At this point this ObjectMonitor cannot be deflated, finish contended enter
|
||||
enter_with_contention_mark(current, contention_mark);
|
||||
enter_with_contention_mark(current, contention_mark, post_jvmti_events);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectMonitor::notify_contended_enter(JavaThread* current) {
|
||||
void ObjectMonitor::notify_contended_enter(JavaThread* current, bool post_jvmti_events) {
|
||||
current->set_current_pending_monitor(this);
|
||||
|
||||
DTRACE_MONITOR_PROBE(contended__enter, this, object(), current);
|
||||
if (JvmtiExport::should_post_monitor_contended_enter()) {
|
||||
if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_enter()) {
|
||||
JvmtiExport::post_monitor_contended_enter(current, this);
|
||||
|
||||
// The current thread does not yet own the monitor and does not
|
||||
@ -540,7 +531,7 @@ void ObjectMonitor::notify_contended_enter(JavaThread* current) {
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm) {
|
||||
void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm, bool post_jvmti_events) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
assert(!has_owner(current), "must be");
|
||||
assert(cm._monitor == this, "must be");
|
||||
@ -564,7 +555,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito
|
||||
ContinuationEntry* ce = current->last_continuation();
|
||||
bool is_virtual = ce != nullptr && ce->is_virtual_thread();
|
||||
if (is_virtual) {
|
||||
notify_contended_enter(current);
|
||||
notify_contended_enter(current, post_jvmti_events);
|
||||
result = Continuation::try_preempt(current, ce->cont_oop(current));
|
||||
if (result == freeze_ok) {
|
||||
bool acquired = vthread_monitor_enter(current);
|
||||
@ -573,7 +564,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito
|
||||
// _entry_list so cancel preemption. We will still go through the preempt stub
|
||||
// but instead of unmounting we will call thaw to continue execution.
|
||||
current->set_preemption_cancelled(true);
|
||||
if (JvmtiExport::should_post_monitor_contended_entered()) {
|
||||
if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) {
|
||||
// We are going to call thaw again after this and finish the VMTS
|
||||
// transition so no need to do it here. We will post the event there.
|
||||
current->set_contended_entered_monitor(this);
|
||||
@ -610,7 +601,8 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito
|
||||
// states will still report that the thread is blocked trying to
|
||||
// acquire it.
|
||||
// If there is a suspend request, ExitOnSuspend will exit the OM
|
||||
// and set the OM as pending.
|
||||
// and set the OM as pending, the thread will not be reported as
|
||||
// having "-locked" the monitor.
|
||||
}
|
||||
if (!eos.exited()) {
|
||||
// ExitOnSuspend did not exit the OM
|
||||
@ -644,7 +636,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito
|
||||
// spinning we could increment JVMStat counters, etc.
|
||||
|
||||
DTRACE_MONITOR_PROBE(contended__entered, this, object(), current);
|
||||
if (JvmtiExport::should_post_monitor_contended_entered()) {
|
||||
if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) {
|
||||
JvmtiExport::post_monitor_contended_entered(current, this);
|
||||
|
||||
// The current thread already owns the monitor and is not going to
|
||||
@ -1102,11 +1094,10 @@ void ObjectMonitor::enter_internal(JavaThread* current) {
|
||||
|
||||
void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentNode) {
|
||||
assert(current != nullptr, "invariant");
|
||||
assert(current->thread_state() != _thread_blocked, "invariant");
|
||||
assert(current->thread_state() == _thread_blocked, "invariant");
|
||||
assert(currentNode != nullptr, "invariant");
|
||||
assert(currentNode->_thread == current, "invariant");
|
||||
assert(_waiters > 0, "invariant");
|
||||
assert_mark_word_consistency();
|
||||
|
||||
// If there are unmounted virtual threads ahead in the _entry_list we want
|
||||
// to do a timed-park instead to alleviate some deadlock cases where one
|
||||
@ -1142,22 +1133,15 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN
|
||||
|
||||
{
|
||||
OSThreadContendState osts(current->osthread());
|
||||
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
{
|
||||
ClearSuccOnSuspend csos(this);
|
||||
ThreadBlockInVMPreprocess<ClearSuccOnSuspend> tbivs(current, csos, true /* allow_suspend */);
|
||||
if (do_timed_parked) {
|
||||
current->_ParkEvent->park(recheck_interval);
|
||||
// Increase the recheck_interval, but clamp the value.
|
||||
recheck_interval *= 8;
|
||||
if (recheck_interval > MAX_RECHECK_INTERVAL) {
|
||||
recheck_interval = MAX_RECHECK_INTERVAL;
|
||||
}
|
||||
} else {
|
||||
current->_ParkEvent->park();
|
||||
if (do_timed_parked) {
|
||||
current->_ParkEvent->park(recheck_interval);
|
||||
// Increase the recheck_interval, but clamp the value.
|
||||
recheck_interval *= 8;
|
||||
if (recheck_interval > MAX_RECHECK_INTERVAL) {
|
||||
recheck_interval = MAX_RECHECK_INTERVAL;
|
||||
}
|
||||
} else {
|
||||
current->_ParkEvent->park();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1184,7 +1168,6 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN
|
||||
|
||||
// Current has acquired the lock -- Unlink current from the _entry_list.
|
||||
assert(has_owner(current), "invariant");
|
||||
assert_mark_word_consistency();
|
||||
unlink_after_acquire(current, currentNode);
|
||||
if (has_successor(current)) clear_successor();
|
||||
assert(!has_successor(current), "invariant");
|
||||
@ -1883,7 +1866,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
// while (!timeout && !interrupted && node.TState == TS_WAIT) park()
|
||||
|
||||
int ret = OS_OK;
|
||||
bool was_notified = false;
|
||||
bool was_notified = true;
|
||||
|
||||
// Need to check interrupt state whilst still _thread_in_vm
|
||||
bool interrupted = interruptible && current->is_interrupted(false);
|
||||
@ -1895,8 +1878,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
{
|
||||
ClearSuccOnSuspend csos(this);
|
||||
ThreadBlockInVMPreprocess<ClearSuccOnSuspend> tbivs(current, csos, true /* allow_suspend */);
|
||||
ThreadBlockInVM tbivm(current, false /* allow_suspend */);
|
||||
if (interrupted || HAS_PENDING_EXCEPTION) {
|
||||
// Intentionally empty
|
||||
} else if (node.TState == ObjectWaiter::TS_WAIT) {
|
||||
@ -1928,17 +1910,16 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
if (node.TState == ObjectWaiter::TS_WAIT) {
|
||||
dequeue_specific_waiter(&node); // unlink from wait_set
|
||||
node.TState = ObjectWaiter::TS_RUN;
|
||||
was_notified = false;
|
||||
}
|
||||
}
|
||||
|
||||
// The thread is now either on off-list (TS_RUN),
|
||||
// The thread is now either off-list (TS_RUN),
|
||||
// or on the entry_list (TS_ENTER).
|
||||
// The Node's TState variable is stable from the perspective of this thread.
|
||||
// No other threads will asynchronously modify TState.
|
||||
guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant");
|
||||
OrderAccess::loadload();
|
||||
if (has_successor(current)) clear_successor();
|
||||
was_notified = node.TState == ObjectWaiter::TS_ENTER;
|
||||
|
||||
// Reentry phase -- reacquire the monitor.
|
||||
// re-enter contended monitor after object.wait().
|
||||
@ -1947,27 +1928,19 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
// although the raw address of the object may have changed.
|
||||
// (Don't cache naked oops over safepoints, of course).
|
||||
|
||||
// post monitor waited event. Note that this is past-tense, we are done waiting.
|
||||
if (JvmtiExport::should_post_monitor_waited()) {
|
||||
JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT);
|
||||
// Post monitor waited event. Note that this is past-tense, we are done waiting.
|
||||
// An event could have been enabled after notification, in this case
|
||||
// a thread will have TS_ENTER state and posting the event may hit a suspension point.
|
||||
// From a debugging perspective, it is more important to have no missing events.
|
||||
if (interruptible && JvmtiExport::should_post_monitor_waited() && node.TState != ObjectWaiter::TS_ENTER) {
|
||||
|
||||
if (was_notified && has_successor(current)) {
|
||||
// In this part of the monitor wait-notify-reenter protocol it
|
||||
// is possible (and normal) for another thread to do a fastpath
|
||||
// monitor enter-exit while this thread is still trying to get
|
||||
// to the reenter portion of the protocol.
|
||||
//
|
||||
// The ObjectMonitor was notified and the current thread is
|
||||
// the successor which also means that an unpark() has already
|
||||
// been done. The JVMTI_EVENT_MONITOR_WAITED event handler can
|
||||
// consume the unpark() that was done when the successor was
|
||||
// set because the same ParkEvent is shared between Java
|
||||
// monitors and JVM/TI RawMonitors (for now).
|
||||
//
|
||||
// We redo the unpark() to ensure forward progress, i.e., we
|
||||
// don't want all pending threads hanging (parked) with none
|
||||
// entering the unlocked monitor.
|
||||
current->_ParkEvent->unpark();
|
||||
// Process suspend requests now if any, before posting the event.
|
||||
{
|
||||
ThreadBlockInVM tbvm(current, true);
|
||||
}
|
||||
// Re-check the condition as the monitor waited events can be disabled whilst thread was suspended.
|
||||
if (JvmtiExport::should_post_monitor_waited()) {
|
||||
JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1986,8 +1959,30 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
NoPreemptMark npm(current);
|
||||
enter(current);
|
||||
} else {
|
||||
// This means the thread has been un-parked and added to the entry_list
|
||||
// in notify_internal, i.e. notified while waiting.
|
||||
guarantee(v == ObjectWaiter::TS_ENTER, "invariant");
|
||||
reenter_internal(current, &node);
|
||||
ExitOnSuspend eos(this);
|
||||
{
|
||||
ThreadBlockInVMPreprocess<ExitOnSuspend> tbivs(current, eos, true /* allow_suspend */);
|
||||
reenter_internal(current, &node);
|
||||
// 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.
|
||||
// If there is a suspend request, ExitOnSuspend will exit the OM
|
||||
// and set the OM as pending, the thread will not be reported as
|
||||
// having "-locked" the monitor.
|
||||
}
|
||||
if (eos.exited()) {
|
||||
// ExitOnSuspend exit the OM
|
||||
assert(!has_owner(current), "invariant");
|
||||
guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant");
|
||||
current->set_current_pending_monitor(nullptr);
|
||||
enter(current, false /* post_jvmti_events */);
|
||||
}
|
||||
assert(has_owner(current), "invariant");
|
||||
node.wait_reenter_end(this);
|
||||
}
|
||||
|
||||
@ -2041,6 +2036,8 @@ bool ObjectMonitor::notify_internal(JavaThread* current) {
|
||||
ObjectWaiter* iterator = dequeue_waiter();
|
||||
if (iterator != nullptr) {
|
||||
guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
|
||||
iterator->_notifier_tid = JFR_THREAD_ID(current);
|
||||
did_notify = true;
|
||||
|
||||
if (iterator->is_vthread()) {
|
||||
oop vthread = iterator->vthread();
|
||||
@ -2056,45 +2053,55 @@ bool ObjectMonitor::notify_internal(JavaThread* current) {
|
||||
old_state == java_lang_VirtualThread::TIMED_WAIT) {
|
||||
java_lang_VirtualThread::cmpxchg_state(vthread, old_state, java_lang_VirtualThread::BLOCKED);
|
||||
}
|
||||
// Increment counter *before* adding the vthread to the _entry_list.
|
||||
// Adding to _entry_list uses Atomic::cmpxchg() which already provides
|
||||
// a fence that prevents reordering of the stores.
|
||||
inc_unmounted_vthreads();
|
||||
if (!JvmtiExport::should_post_monitor_waited()) {
|
||||
// Increment counter *before* adding the vthread to the _entry_list.
|
||||
// Adding to _entry_list uses Atomic::cmpxchg() which already provides
|
||||
// a fence that prevents reordering of the stores.
|
||||
inc_unmounted_vthreads();
|
||||
add_to_entry_list(current, iterator);
|
||||
} else {
|
||||
iterator->TState = ObjectWaiter::TS_RUN;
|
||||
if (java_lang_VirtualThread::set_onWaitingList(vthread, vthread_list_head())) {
|
||||
ParkEvent* pe = ObjectMonitor::vthread_unparker_ParkEvent();
|
||||
pe->unpark();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!JvmtiExport::should_post_monitor_waited()) {
|
||||
add_to_entry_list(current, iterator);
|
||||
// Read counter *after* adding the thread to the _entry_list.
|
||||
// Adding to _entry_list uses Atomic::cmpxchg() which already provides
|
||||
// a fence that prevents this load from floating up previous store.
|
||||
if (has_unmounted_vthreads()) {
|
||||
// Wake up the thread to alleviate some deadlock cases where the successor
|
||||
// that will be picked up when this thread releases the monitor is an unmounted
|
||||
// virtual thread that cannot run due to having run out of carriers. Upon waking
|
||||
// up, the thread will call reenter_internal() which will use timed-park in case
|
||||
// there is contention and there are still vthreads in the _entry_list.
|
||||
// If the target was interrupted or the wait timed-out at the same time, it could
|
||||
// have reached reenter_internal and read a false value of has_unmounted_vthreads()
|
||||
// before we added it to the _entry_list above. To deal with that case, we set _do_timed_park
|
||||
// which will be read by the target on the next loop iteration in reenter_internal.
|
||||
iterator->_do_timed_park = true;
|
||||
JavaThread* t = iterator->thread();
|
||||
t->_ParkEvent->unpark();
|
||||
}
|
||||
iterator->wait_reenter_begin(this);
|
||||
} else {
|
||||
iterator->TState = ObjectWaiter::TS_RUN;
|
||||
JavaThread* t = iterator->thread();
|
||||
assert(t != nullptr, "");
|
||||
t->_ParkEvent->unpark();
|
||||
}
|
||||
}
|
||||
|
||||
iterator->_notifier_tid = JFR_THREAD_ID(current);
|
||||
did_notify = true;
|
||||
add_to_entry_list(current, iterator);
|
||||
|
||||
// _wait_set_lock protects the wait queue, not the entry_list. We could
|
||||
// move the add-to-entry_list operation, above, outside the critical section
|
||||
// protected by _wait_set_lock. In practice that's not useful. With the
|
||||
// exception of wait() timeouts and interrupts the monitor owner
|
||||
// exception of wait() timeouts and interrupts the monitor owner
|
||||
// is the only thread that grabs _wait_set_lock. There's almost no contention
|
||||
// on _wait_set_lock so it's not profitable to reduce the length of the
|
||||
// critical section.
|
||||
|
||||
if (!iterator->is_vthread()) {
|
||||
iterator->wait_reenter_begin(this);
|
||||
|
||||
// Read counter *after* adding the thread to the _entry_list.
|
||||
// Adding to _entry_list uses Atomic::cmpxchg() which already provides
|
||||
// a fence that prevents this load from floating up previous store.
|
||||
if (has_unmounted_vthreads()) {
|
||||
// Wake up the thread to alleviate some deadlock cases where the successor
|
||||
// that will be picked up when this thread releases the monitor is an unmounted
|
||||
// virtual thread that cannot run due to having run out of carriers. Upon waking
|
||||
// up, the thread will call reenter_internal() which will use timed-park in case
|
||||
// there is contention and there are still vthreads in the _entry_list.
|
||||
// If the target was interrupted or the wait timed-out at the same time, it could
|
||||
// have reached reenter_internal and read a false value of has_unmounted_vthreads()
|
||||
// before we added it to the _entry_list above. To deal with that case, we set _do_timed_park
|
||||
// which will be read by the target on the next loop iteration in reenter_internal.
|
||||
iterator->_do_timed_park = true;
|
||||
JavaThread* t = iterator->thread();
|
||||
t->_ParkEvent->unpark();
|
||||
}
|
||||
}
|
||||
}
|
||||
return did_notify;
|
||||
}
|
||||
@ -2221,19 +2228,22 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node
|
||||
// The first time we run after being preempted on Object.wait() we
|
||||
// need to check if we were interrupted or the wait timed-out, and
|
||||
// in that case remove ourselves from the _wait_set queue.
|
||||
bool was_notified = true;
|
||||
if (node->TState == ObjectWaiter::TS_WAIT) {
|
||||
SpinCriticalSection scs(&_wait_set_lock);
|
||||
if (node->TState == ObjectWaiter::TS_WAIT) {
|
||||
dequeue_specific_waiter(node); // unlink from wait_set
|
||||
node->TState = ObjectWaiter::TS_RUN;
|
||||
was_notified = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If this was an interrupted case, set the _interrupted boolean so that
|
||||
// once we re-acquire the monitor we know if we need to throw IE or not.
|
||||
ObjectWaiter::TStates state = node->TState;
|
||||
bool was_notified = state == ObjectWaiter::TS_ENTER;
|
||||
assert(was_notified || state == ObjectWaiter::TS_RUN, "");
|
||||
assert(was_notified || state == ObjectWaiter::TS_RUN,
|
||||
"was not notified and is not in the right state: state = %s",
|
||||
node->getTStateName(state));
|
||||
node->_interrupted = node->_interruptible && !was_notified && current->is_interrupted(false);
|
||||
|
||||
// Post JFR and JVMTI events. If non-interruptible we are in
|
||||
@ -2246,7 +2256,10 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node
|
||||
// Mark that we are at reenter so that we don't call this method again.
|
||||
node->_at_reenter = true;
|
||||
|
||||
if (!was_notified) {
|
||||
// We check the state rather than was_notified because, when JVMTI
|
||||
// monitor_waited event is enabled, the notifier only unparks the waiter
|
||||
// without adding it to the entry_list.
|
||||
if (state == ObjectWaiter::TS_RUN) {
|
||||
bool acquired = vthread_monitor_enter(current, node);
|
||||
if (acquired) {
|
||||
guarantee(_recursions == 0, "invariant");
|
||||
@ -2537,6 +2550,23 @@ ObjectWaiter::ObjectWaiter(JavaThread* current) {
|
||||
_active = false;
|
||||
}
|
||||
|
||||
const char* ObjectWaiter::getTStateName(ObjectWaiter::TStates state) {
|
||||
switch (state) {
|
||||
case ObjectWaiter::TS_UNDEF:
|
||||
return "TS_UNDEF";
|
||||
case ObjectWaiter::TS_READY:
|
||||
return "TS_READY";
|
||||
case ObjectWaiter::TS_RUN:
|
||||
return "TS_RUN";
|
||||
case ObjectWaiter::TS_WAIT:
|
||||
return "TS_WAIT";
|
||||
case ObjectWaiter::TS_ENTER:
|
||||
return "TS_ENTER";
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
ObjectWaiter::ObjectWaiter(oop vthread, ObjectMonitor* mon) : ObjectWaiter(nullptr) {
|
||||
assert(oopDesc::is_oop(vthread), "");
|
||||
_vthread = OopHandle(JavaThread::thread_oop_storage(), vthread);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -40,7 +40,6 @@ class ParkEvent;
|
||||
class BasicLock;
|
||||
class ContinuationWrapper;
|
||||
|
||||
|
||||
class ObjectWaiter : public CHeapObj<mtThread> {
|
||||
public:
|
||||
enum TStates : uint8_t { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER };
|
||||
@ -72,7 +71,7 @@ class ObjectWaiter : public CHeapObj<mtThread> {
|
||||
oop vthread() const;
|
||||
void wait_reenter_begin(ObjectMonitor *mon);
|
||||
void wait_reenter_end(ObjectMonitor *mon);
|
||||
|
||||
const char* getTStateName(TStates state);
|
||||
void set_bad_pointers() {
|
||||
#ifdef ASSERT
|
||||
this->_prev = (ObjectWaiter*) badAddressVal;
|
||||
@ -352,7 +351,6 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
|
||||
// returns false and throws IllegalMonitorStateException (IMSE).
|
||||
bool check_owner(TRAPS);
|
||||
|
||||
private:
|
||||
class ExitOnSuspend {
|
||||
protected:
|
||||
ObjectMonitor* _om;
|
||||
@ -362,23 +360,16 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
|
||||
void operator()(JavaThread* current);
|
||||
bool exited() { return _om_exited; }
|
||||
};
|
||||
class ClearSuccOnSuspend {
|
||||
protected:
|
||||
ObjectMonitor* _om;
|
||||
public:
|
||||
ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {}
|
||||
void operator()(JavaThread* current);
|
||||
};
|
||||
|
||||
bool enter_is_async_deflating();
|
||||
void notify_contended_enter(JavaThread *current);
|
||||
void notify_contended_enter(JavaThread *current, bool post_jvmti_events = true);
|
||||
public:
|
||||
void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark);
|
||||
bool enter_for(JavaThread* locking_thread);
|
||||
bool enter(JavaThread* current);
|
||||
bool enter(JavaThread* current, bool post_jvmti_events = true);
|
||||
bool try_enter(JavaThread* current, bool check_for_recursion = true);
|
||||
bool spin_enter(JavaThread* current);
|
||||
void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark);
|
||||
void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark, bool post_jvmti_events = true);
|
||||
void exit(JavaThread* current, bool not_suspended = true);
|
||||
bool resume_operation(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont);
|
||||
void wait(jlong millis, bool interruptible, TRAPS);
|
||||
|
||||
@ -1,371 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 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
|
||||
* 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 4413752 8262881
|
||||
* @summary Test SuspendThread with ObjectMonitor wait.
|
||||
* @requires vm.jvmti
|
||||
* @library /test/lib
|
||||
* @compile SuspendWithObjectMonitorWait.java
|
||||
* @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
//
|
||||
// main waiter resumer
|
||||
// ================= ================== ===================
|
||||
// launch waiter
|
||||
// <launch returns> waiter running
|
||||
// launch resumer enter threadLock
|
||||
// <launch returns> threadLock.wait() resumer running
|
||||
// enter threadLock : wait for notify
|
||||
// threadLock.notify wait finishes :
|
||||
// : reenter blocks :
|
||||
// suspend waiter <suspended> :
|
||||
// exit threadLock : :
|
||||
// <ready to test> : :
|
||||
// : : :
|
||||
// notify resumer : wait finishes
|
||||
// join resumer : enter threadLock
|
||||
// : <resumed> resume waiter
|
||||
// : : exit threadLock
|
||||
// : reenter threadLock :
|
||||
// <join returns> : resumer exits
|
||||
// join waiter :
|
||||
// <join returns> waiter exits
|
||||
//
|
||||
|
||||
public class SuspendWithObjectMonitorWait {
|
||||
private static final String AGENT_LIB = "SuspendWithObjectMonitorWait";
|
||||
private static final int exit_delta = 95;
|
||||
|
||||
private static final int DEF_TIME_MAX = 60; // default max # secs to test
|
||||
private static final int JOIN_MAX = 30; // max # secs to wait for join
|
||||
|
||||
public static final int TS_INIT = 1; // initial testState
|
||||
public static final int TS_WAITER_RUNNING = 2; // waiter is running
|
||||
public static final int TS_RESUMER_RUNNING = 3; // resumer is running
|
||||
public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock
|
||||
public static final int TS_CALL_SUSPEND = 5; // call suspend on contender
|
||||
public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter
|
||||
public static final int TS_CALL_RESUME = 7; // call resume on waiter
|
||||
public static final int TS_WAITER_DONE = 8; // waiter has run; done
|
||||
|
||||
public static Object barrierLaunch = new Object(); // controls thread launch
|
||||
public static Object barrierResumer = new Object(); // controls resumer
|
||||
public static Object threadLock = new Object(); // testing object
|
||||
|
||||
public static long count = 0;
|
||||
public static boolean printDebug = false;
|
||||
public volatile static int testState;
|
||||
|
||||
private static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr);
|
||||
native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
System.loadLibrary(AGENT_LIB);
|
||||
log("Loaded library: " + AGENT_LIB);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
log("Failed to load library: " + AGENT_LIB);
|
||||
log("java.library.path: " + System.getProperty("java.library.path"));
|
||||
throw ule;
|
||||
}
|
||||
|
||||
int timeMax = 0;
|
||||
if (args.length == 0) {
|
||||
timeMax = DEF_TIME_MAX;
|
||||
} else {
|
||||
int argIndex = 0;
|
||||
int argsLeft = args.length;
|
||||
if (args[0].equals("-p")) {
|
||||
printDebug = true;
|
||||
argIndex = 1;
|
||||
argsLeft--;
|
||||
}
|
||||
if (argsLeft == 0) {
|
||||
timeMax = DEF_TIME_MAX;
|
||||
} else if (argsLeft == 1) {
|
||||
try {
|
||||
timeMax = Integer.parseUnsignedInt(args[argIndex]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("'" + args[argIndex] +
|
||||
"': invalid timeMax value.");
|
||||
usage();
|
||||
}
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(run(timeMax, System.out) + exit_delta);
|
||||
}
|
||||
|
||||
public static void logDebug(String mesg) {
|
||||
if (printDebug) {
|
||||
System.err.println(Thread.currentThread().getName() + ": " + mesg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void usage() {
|
||||
System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]");
|
||||
System.err.println("where:");
|
||||
System.err.println(" -p ::= print debug info");
|
||||
System.err.println(" time_max ::= max looping time in seconds");
|
||||
System.err.println(" (default is " + DEF_TIME_MAX +
|
||||
" seconds)");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static int run(int timeMax, PrintStream out) {
|
||||
return (new SuspendWithObjectMonitorWait()).doWork(timeMax, out);
|
||||
}
|
||||
|
||||
public static void checkTestState(int exp) {
|
||||
if (testState != exp) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("Unexpected test state value: "
|
||||
+ "expected=" + exp + " actual=" + testState);
|
||||
}
|
||||
}
|
||||
|
||||
public int doWork(int timeMax, PrintStream out) {
|
||||
SuspendWithObjectMonitorWaitWorker waiter; // waiter thread
|
||||
SuspendWithObjectMonitorWaitWorker resumer; // resumer thread
|
||||
|
||||
System.out.println("About to execute for " + timeMax + " seconds.");
|
||||
|
||||
long start_time = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
|
||||
count++;
|
||||
testState = TS_INIT; // starting the test loop
|
||||
|
||||
// launch the waiter thread
|
||||
synchronized (barrierLaunch) {
|
||||
waiter = new SuspendWithObjectMonitorWaitWorker("waiter");
|
||||
waiter.start();
|
||||
|
||||
while (testState != TS_WAITER_RUNNING) {
|
||||
try {
|
||||
barrierLaunch.wait(0); // wait until it is running
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// launch the resumer thread
|
||||
synchronized (barrierLaunch) {
|
||||
resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter);
|
||||
resumer.start();
|
||||
|
||||
while (testState != TS_RESUMER_RUNNING) {
|
||||
try {
|
||||
barrierLaunch.wait(0); // wait until it is running
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkTestState(TS_RESUMER_RUNNING);
|
||||
|
||||
// The waiter thread was synchronized on threadLock before it
|
||||
// set TS_WAITER_RUNNING and notified barrierLaunch above so
|
||||
// we cannot enter threadLock until the waiter thread calls
|
||||
// threadLock.wait().
|
||||
synchronized (threadLock) {
|
||||
// notify waiter thread so it can try to reenter threadLock
|
||||
testState = TS_READY_TO_NOTIFY;
|
||||
threadLock.notify();
|
||||
|
||||
// wait for the waiter thread to block
|
||||
logDebug("before contended enter wait");
|
||||
int retCode = wait4ContendedEnter(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI GetThreadState: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("done contended enter wait");
|
||||
|
||||
checkTestState(TS_READY_TO_NOTIFY);
|
||||
testState = TS_CALL_SUSPEND;
|
||||
logDebug("before suspend thread");
|
||||
retCode = suspendThread(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI SuspendThread: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("suspended thread");
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, all of the child threads are running
|
||||
// and we can get to meat of the test:
|
||||
//
|
||||
// - suspended threadLock waiter (trying to reenter)
|
||||
// - a threadLock enter in the resumer thread
|
||||
// - resumption of the waiter thread
|
||||
// - a threadLock enter in the freshly resumed waiter thread
|
||||
//
|
||||
|
||||
synchronized (barrierResumer) {
|
||||
checkTestState(TS_CALL_SUSPEND);
|
||||
|
||||
// tell resumer thread to resume waiter thread
|
||||
testState = TS_READY_TO_RESUME;
|
||||
barrierResumer.notify();
|
||||
|
||||
// Can't call checkTestState() here because the
|
||||
// resumer thread may have already resumed the
|
||||
// waiter thread.
|
||||
}
|
||||
|
||||
try {
|
||||
resumer.join(JOIN_MAX * 1000);
|
||||
if (resumer.isAlive()) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("resumer thread is stuck");
|
||||
}
|
||||
waiter.join(JOIN_MAX * 1000);
|
||||
if (waiter.isAlive()) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("waiter thread is stuck");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
checkTestState(TS_WAITER_DONE);
|
||||
}
|
||||
|
||||
System.out.println("Executed " + count + " loops in " + timeMax +
|
||||
" seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class SuspendWithObjectMonitorWaitWorker extends Thread {
|
||||
private SuspendWithObjectMonitorWaitWorker target; // target for resume operation
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) {
|
||||
super(name);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr);
|
||||
|
||||
public void run() {
|
||||
SuspendWithObjectMonitorWait.logDebug("thread running");
|
||||
|
||||
//
|
||||
// Launch the waiter thread:
|
||||
// - grab the threadLock
|
||||
// - threadLock.wait()
|
||||
// - releases threadLock
|
||||
//
|
||||
if (getName().equals("waiter")) {
|
||||
// grab threadLock before we tell main we are running
|
||||
SuspendWithObjectMonitorWait.logDebug("before enter threadLock");
|
||||
synchronized(SuspendWithObjectMonitorWait.threadLock) {
|
||||
SuspendWithObjectMonitorWait.logDebug("enter threadLock");
|
||||
|
||||
SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT);
|
||||
|
||||
synchronized(SuspendWithObjectMonitorWait.barrierLaunch) {
|
||||
// tell main we are running
|
||||
SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING;
|
||||
SuspendWithObjectMonitorWait.barrierLaunch.notify();
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWait.logDebug("before wait");
|
||||
|
||||
// TS_READY_TO_NOTIFY is set after the main thread has
|
||||
// entered threadLock so a spurious wakeup can't get the
|
||||
// waiter thread out of this threadLock.wait(0) call:
|
||||
while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) {
|
||||
try {
|
||||
SuspendWithObjectMonitorWait.threadLock.wait(0);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWait.logDebug("after wait");
|
||||
|
||||
SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME);
|
||||
SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE;
|
||||
|
||||
SuspendWithObjectMonitorWait.logDebug("exit threadLock");
|
||||
}
|
||||
}
|
||||
//
|
||||
// Launch the resumer thread:
|
||||
// - tries to grab the threadLock (should not block!)
|
||||
// - grabs threadLock
|
||||
// - resumes the waiter thread
|
||||
// - releases threadLock
|
||||
//
|
||||
else if (getName().equals("resumer")) {
|
||||
synchronized(SuspendWithObjectMonitorWait.barrierResumer) {
|
||||
synchronized(SuspendWithObjectMonitorWait.barrierLaunch) {
|
||||
// tell main we are running
|
||||
SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING;
|
||||
SuspendWithObjectMonitorWait.barrierLaunch.notify();
|
||||
}
|
||||
SuspendWithObjectMonitorWait.logDebug("thread waiting");
|
||||
while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) {
|
||||
try {
|
||||
// wait for main to tell us when to continue
|
||||
SuspendWithObjectMonitorWait.barrierResumer.wait(0);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWait.logDebug("before enter threadLock");
|
||||
synchronized(SuspendWithObjectMonitorWait.threadLock) {
|
||||
SuspendWithObjectMonitorWait.logDebug("enter threadLock");
|
||||
|
||||
SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME);
|
||||
SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME;
|
||||
|
||||
// resume the waiter thread so waiter.join() can work
|
||||
SuspendWithObjectMonitorWait.logDebug("before resume thread");
|
||||
int retCode = resumeThread(target);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI ResumeThread: " +
|
||||
"retCode=" + retCode);
|
||||
}
|
||||
SuspendWithObjectMonitorWait.logDebug("resumed thread");
|
||||
|
||||
SuspendWithObjectMonitorWait.logDebug("exit threadLock");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2026, 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.
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class SuspendWithObjectMonitorWaitBase {
|
||||
protected static final String AGENT_LIB = "SuspendWithObjectMonitorWait";
|
||||
protected static final int exit_delta = 95;
|
||||
|
||||
protected static final int DEF_TIME_MAX = 60; // default max # secs to test
|
||||
protected static final int JOIN_MAX = 30; // max # secs to wait for join
|
||||
|
||||
public static final int TS_INIT = 1; // initial testState
|
||||
public static final int TS_WAITER_RUNNING = 2; // waiter is running
|
||||
public static final int TS_RESUMER_RUNNING = 3; // resumer is running
|
||||
public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock
|
||||
public static final int TS_CALL_SUSPEND = 5; // call suspend on contender
|
||||
public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter
|
||||
public static final int TS_CALL_RESUME = 7; // call resume on waiter
|
||||
public static final int TS_WAITER_DONE = 8; // waiter has run; done
|
||||
|
||||
public static Object barrierLaunch = new Object(); // controls thread launch
|
||||
public static Object barrierResumer = new Object(); // controls resumer
|
||||
public static Object threadLock = new Object(); // testing object
|
||||
|
||||
public static long count = 0;
|
||||
public static boolean printDebug = false;
|
||||
public volatile static int testState;
|
||||
|
||||
protected static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr);
|
||||
native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr);
|
||||
|
||||
public static void logDebug(String mesg) {
|
||||
if (printDebug) {
|
||||
System.err.println(Thread.currentThread().getName() + ": " + mesg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void usage() {
|
||||
System.err.println("Usage: " + AGENT_LIB + " test_case [-p] [time_max]");
|
||||
System.err.println("where:");
|
||||
System.err.println(" test_case ::= 1 | 2 | 3");
|
||||
System.err.println(" -p ::= print debug info");
|
||||
System.err.println(" time_max ::= max looping time in seconds");
|
||||
System.err.println(" (default is " + DEF_TIME_MAX +
|
||||
" seconds)");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void checkTestState(int exp) {
|
||||
if (testState != exp) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("Unexpected test state value: "
|
||||
+ "expected=" + exp + " actual=" + testState);
|
||||
}
|
||||
}
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker launchWaiter(long waitTimeout) {
|
||||
SuspendWithObjectMonitorWaitWorker waiter;
|
||||
// launch the waiter thread
|
||||
synchronized (barrierLaunch) {
|
||||
waiter = new SuspendWithObjectMonitorWaitWorker("waiter", waitTimeout);
|
||||
waiter.start();
|
||||
|
||||
while (testState != TS_WAITER_RUNNING) {
|
||||
try {
|
||||
barrierLaunch.wait(0); // wait until it is running
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return waiter;
|
||||
}
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker launchResumer(SuspendWithObjectMonitorWaitWorker waiter) {
|
||||
SuspendWithObjectMonitorWaitWorker resumer;
|
||||
synchronized (barrierLaunch) {
|
||||
resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter);
|
||||
resumer.start();
|
||||
|
||||
while (testState != TS_RESUMER_RUNNING) {
|
||||
try {
|
||||
barrierLaunch.wait(0); // wait until it is running
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return resumer;
|
||||
}
|
||||
|
||||
public void barrierResumerNotify() {
|
||||
synchronized (barrierResumer) {
|
||||
checkTestState(TS_CALL_SUSPEND);
|
||||
|
||||
// tell resumer thread to resume waiter thread
|
||||
testState = TS_READY_TO_RESUME;
|
||||
barrierResumer.notify();
|
||||
|
||||
// Can't call checkTestState() here because the
|
||||
// resumer thread may have already resumed the
|
||||
// waiter thread.
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDown(SuspendWithObjectMonitorWaitWorker resumer, SuspendWithObjectMonitorWaitWorker waiter) {
|
||||
try {
|
||||
resumer.join(JOIN_MAX * 1000);
|
||||
if (resumer.isAlive()) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("resumer thread is stuck");
|
||||
}
|
||||
waiter.join(JOIN_MAX * 1000);
|
||||
if (waiter.isAlive()) {
|
||||
System.err.println("Failure at " + count + " loops.");
|
||||
throw new InternalError("waiter thread is stuck");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
public int run(int timeMax, PrintStream out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
System.err.println("Invalid number of arguments, there should be at least a test_case given.");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (args.length > 3) {
|
||||
System.err.println("Invalid number of arguments, there are too many arguments.");
|
||||
usage();
|
||||
}
|
||||
|
||||
try {
|
||||
System.loadLibrary(AGENT_LIB);
|
||||
log("Loaded library: " + AGENT_LIB);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
log("Failed to load library: " + AGENT_LIB);
|
||||
log("java.library.path: " + System.getProperty("java.library.path"));
|
||||
throw ule;
|
||||
}
|
||||
|
||||
int testCase = 0;
|
||||
int timeMax = 0;
|
||||
for (int argIndex = 0; argIndex < args.length; argIndex++) {
|
||||
if (args[argIndex].equals("-p")) {
|
||||
// Handle optional -p arg regardless of position.
|
||||
printDebug = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testCase == 0) {
|
||||
try {
|
||||
// testCase must be the first non-optional arg.
|
||||
testCase = Integer.parseUnsignedInt(args[argIndex]);
|
||||
log("testCase = " + testCase);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("'" + args[argIndex] +
|
||||
"': invalid test_case value.");
|
||||
usage();
|
||||
}
|
||||
if (testCase < 1 || testCase > 3) {
|
||||
System.err.println("Invalid test_case value: '" + testCase + "'");
|
||||
usage();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argIndex < args.length) {
|
||||
// timeMax is an optional arg.
|
||||
try {
|
||||
timeMax = Integer.parseUnsignedInt(args[argIndex]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("'" + args[argIndex] +
|
||||
"': invalid time_max value.");
|
||||
usage();
|
||||
}
|
||||
} else {
|
||||
timeMax = DEF_TIME_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeMax == 0) {
|
||||
timeMax = DEF_TIME_MAX;
|
||||
}
|
||||
log("timeMax = " + timeMax);
|
||||
|
||||
if (testCase == 0) {
|
||||
// Just -p was given.
|
||||
System.err.println("Invalid number of arguments, no test_case given.");
|
||||
usage();
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWaitBase test = null;
|
||||
switch (testCase) {
|
||||
case 1:
|
||||
test = new SuspendWithObjectMonitorWaitDefault();
|
||||
break;
|
||||
case 2:
|
||||
test = new SuspendWithObjectMonitorWaitReentryPartFirst();
|
||||
break;
|
||||
case 3:
|
||||
test = new SuspendWithObjectMonitorWaitReentryPartSecond();
|
||||
break;
|
||||
default:
|
||||
// Impossible
|
||||
break;
|
||||
}
|
||||
int result = test.run(timeMax, System.out);
|
||||
System.exit(result + exit_delta);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2026, 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 4413752 8262881
|
||||
* @summary Test SuspendThread with ObjectMonitor wait.
|
||||
* @requires vm.jvmti
|
||||
* @library /test/lib
|
||||
* @compile SuspendWithObjectMonitorWaitDefault.java
|
||||
* @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitDefault 1
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
//
|
||||
// SuspendWithObjectMonitorWaitDefault algorithm:
|
||||
//
|
||||
// main waiter resumer
|
||||
// ================= ================== ===================
|
||||
// launch waiter
|
||||
// <launch returns> waiter running
|
||||
// launch resumer enter threadLock
|
||||
// <launch returns> threadLock.wait() resumer running
|
||||
// enter threadLock : wait for notify
|
||||
// threadLock.notify wait finishes :
|
||||
// : reenter blocks :
|
||||
// suspend waiter <suspended> :
|
||||
// exit threadLock : :
|
||||
// <ready to test> : :
|
||||
// : : :
|
||||
// notify resumer : wait finishes
|
||||
// join resumer : enter threadLock
|
||||
// : <resumed> resume waiter
|
||||
// : : exit threadLock
|
||||
// : reenter threadLock :
|
||||
// <join returns> : resumer exits
|
||||
// join waiter :
|
||||
// <join returns> waiter exits
|
||||
//
|
||||
|
||||
public class SuspendWithObjectMonitorWaitDefault extends SuspendWithObjectMonitorWaitBase {
|
||||
|
||||
@Override
|
||||
public int run(int timeMax, PrintStream out) {
|
||||
return doWork1(timeMax, out);
|
||||
}
|
||||
|
||||
// Default scenario, the resumer thread is always able to grab the threadLock once notified by the main thread.
|
||||
public int doWork1(int timeMax, PrintStream out) {
|
||||
SuspendWithObjectMonitorWaitWorker waiter; // waiter thread
|
||||
SuspendWithObjectMonitorWaitWorker resumer; // resumer thread
|
||||
|
||||
System.out.println("Test 1: About to execute for " + timeMax + " seconds.");
|
||||
|
||||
long start_time = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
|
||||
count++;
|
||||
testState = TS_INIT; // starting the test loop
|
||||
|
||||
// launch the waiter thread
|
||||
waiter = launchWaiter(0);
|
||||
|
||||
// launch the resumer thread
|
||||
resumer = launchResumer(waiter);
|
||||
|
||||
checkTestState(TS_RESUMER_RUNNING);
|
||||
|
||||
// The waiter thread was synchronized on threadLock before it
|
||||
// set TS_WAITER_RUNNING and notified barrierLaunch above so
|
||||
// we cannot enter threadLock until the waiter thread calls
|
||||
// threadLock.wait().
|
||||
synchronized (threadLock) {
|
||||
// notify waiter thread so it can try to reenter threadLock
|
||||
testState = TS_READY_TO_NOTIFY;
|
||||
threadLock.notify();
|
||||
|
||||
// wait for the waiter thread to block
|
||||
logDebug("before contended enter wait");
|
||||
int retCode = wait4ContendedEnter(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI GetThreadState: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("done contended enter wait");
|
||||
|
||||
checkTestState(TS_READY_TO_NOTIFY);
|
||||
testState = TS_CALL_SUSPEND;
|
||||
logDebug("before suspend thread");
|
||||
retCode = suspendThread(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI SuspendThread: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("suspended thread");
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, all of the child threads are running
|
||||
// and we can get to meat of the test:
|
||||
//
|
||||
// - suspended threadLock waiter (trying to reenter)
|
||||
// - a threadLock enter in the resumer thread
|
||||
// - resumption of the waiter thread
|
||||
// - a threadLock enter in the freshly resumed waiter thread
|
||||
//
|
||||
barrierResumerNotify();
|
||||
|
||||
shutDown(waiter ,resumer);
|
||||
checkTestState(TS_WAITER_DONE);
|
||||
}
|
||||
|
||||
System.out.println("Executed " + count + " loops in " + timeMax +
|
||||
" seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2026, 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 4413752 8262881
|
||||
* @summary Test SuspendThread with ObjectMonitor wait.
|
||||
* @requires vm.jvmti
|
||||
* @library /test/lib
|
||||
* @compile SuspendWithObjectMonitorWaitReentryPartFirst.java
|
||||
* @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartFirst 2
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
//
|
||||
// SuspendWithObjectMonitorWaitReentryPartFirst algorithm:
|
||||
//
|
||||
// main waiter resumer
|
||||
// ================= ================== ===================
|
||||
// launch waiter
|
||||
// <launch returns> waiter running
|
||||
// launch resumer enter threadLock
|
||||
// <launch returns> threadLock.wait() resumer running
|
||||
// enter threadLock : wait for notify
|
||||
// threadLock.notify wait finishes :
|
||||
// : reenter blocks :
|
||||
// suspend waiter <suspended> :
|
||||
// <ready to test> : :
|
||||
// : : :
|
||||
// notify resumer : wait finishes
|
||||
// delay 1-second : :
|
||||
// exit threadLock : :
|
||||
// join resumer : enter threadLock
|
||||
// : <resumed> resume waiter
|
||||
// : : exit threadLock
|
||||
// : reenter threadLock :
|
||||
// <join returns> : resumer exits
|
||||
// join waiter :
|
||||
// <join returns> waiter exits
|
||||
//
|
||||
// Note: The sleep(1-second) in main along with the delayed exit
|
||||
// of threadLock in main forces the resumer thread to reach
|
||||
// "enter threadLock" and block. This difference from the default scenario
|
||||
// forces the resumer thread to be contending for threadLock
|
||||
// while the waiter thread is in threadLock.wait() increasing
|
||||
// stress on the monitor sub-system.
|
||||
//
|
||||
|
||||
public class SuspendWithObjectMonitorWaitReentryPartFirst extends SuspendWithObjectMonitorWaitBase {
|
||||
|
||||
@Override
|
||||
public int run(int timeMax, PrintStream out) {
|
||||
return doWork2(timeMax, out);
|
||||
}
|
||||
|
||||
// Notify the resumer while holding the threadLock.
|
||||
public int doWork2(int timeMax, PrintStream out) {
|
||||
SuspendWithObjectMonitorWaitWorker waiter; // waiter thread
|
||||
SuspendWithObjectMonitorWaitWorker resumer; // resumer thread
|
||||
|
||||
System.out.println("Test 2: About to execute for " + timeMax + " seconds.");
|
||||
|
||||
long start_time = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
|
||||
count++;
|
||||
testState = TS_INIT; // starting the test loop
|
||||
|
||||
// launch the waiter thread
|
||||
waiter = launchWaiter(0);
|
||||
|
||||
// launch the resumer thread
|
||||
resumer = launchResumer(waiter);
|
||||
|
||||
checkTestState(TS_RESUMER_RUNNING);
|
||||
|
||||
// The waiter thread was synchronized on threadLock before it
|
||||
// set TS_WAITER_RUNNING and notified barrierLaunch above so
|
||||
// we cannot enter threadLock until the waiter thread calls
|
||||
// threadLock.wait().
|
||||
synchronized (threadLock) {
|
||||
// notify waiter thread so it can try to reenter threadLock
|
||||
testState = TS_READY_TO_NOTIFY;
|
||||
threadLock.notify();
|
||||
|
||||
// wait for the waiter thread to block
|
||||
logDebug("before contended enter wait");
|
||||
int retCode = wait4ContendedEnter(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI GetThreadState: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("done contended enter wait");
|
||||
|
||||
checkTestState(TS_READY_TO_NOTIFY);
|
||||
testState = TS_CALL_SUSPEND;
|
||||
logDebug("before suspend thread");
|
||||
retCode = suspendThread(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI SuspendThread: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("suspended thread");
|
||||
|
||||
//
|
||||
// At this point, all of the child threads are running
|
||||
// and we can get to meat of the test:
|
||||
//
|
||||
// - suspended threadLock waiter (trying to reenter)
|
||||
// - a blocked threadLock enter in the resumer thread while the
|
||||
// threadLock is held by the main thread.
|
||||
// - resumption of the waiter thread
|
||||
// - a threadLock enter in the freshly resumed waiter thread
|
||||
//
|
||||
barrierResumerNotify();
|
||||
try {
|
||||
// Delay for 1-second while holding the threadLock to force the
|
||||
// resumer thread to block on entering the threadLock.
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
shutDown(waiter ,resumer);
|
||||
checkTestState(TS_WAITER_DONE);
|
||||
}
|
||||
|
||||
System.out.println("Executed " + count + " loops in " + timeMax +
|
||||
" seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2026, 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 4413752 8262881
|
||||
* @summary Test SuspendThread with ObjectMonitor wait.
|
||||
* @requires vm.jvmti
|
||||
* @library /test/lib
|
||||
* @compile SuspendWithObjectMonitorWaitReentryPartSecond.java
|
||||
* @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartSecond 3
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
//
|
||||
// SuspendWithObjectMonitorWaitReentryPartSecond algorithm:
|
||||
//
|
||||
// main waiter resumer
|
||||
// =================== ====================== ===================
|
||||
// launch waiter
|
||||
// <launch returns> waiter running
|
||||
// launch resumer enter threadLock
|
||||
// <launch returns> while !READY_TO_NOTIFY resumer running
|
||||
// : threadLock.wait(1) wait for notify
|
||||
// enter threadLock : :
|
||||
// set READY_TO_NOTIFY :
|
||||
// threadLock.notify wait finishes :
|
||||
// : delay 200ms reenter blocks :
|
||||
// suspend waiter <suspended> :
|
||||
// <ready to test> : :
|
||||
// : : :
|
||||
// notify resumer : wait finishes
|
||||
// delay 1-second : :
|
||||
// exit threadLock : :
|
||||
// join resumer : enter threadLock
|
||||
// : <resumed> resume waiter
|
||||
// : : exit threadLock
|
||||
// : reenter threadLock :
|
||||
// <join returns> : resumer exits
|
||||
// join waiter :
|
||||
// <join returns> waiter exits
|
||||
//
|
||||
// Note: The sleep(1-second) in main along with the delayed exit
|
||||
// of threadLock in main forces the resumer thread to reach
|
||||
// "enter threadLock" and block. This difference from the default scenario
|
||||
// forces the resumer thread to be contending for threadLock
|
||||
// while the waiter thread is in the threadLock.wait(1) tight
|
||||
// loop increasing stress on the monitor sub-system.
|
||||
//
|
||||
// Note: sleep(200ms) here while holding the threadLock to allow the
|
||||
// waiter thread's timed wait to finish before we attempt to
|
||||
// suspend the waiter thread.
|
||||
//
|
||||
|
||||
public class SuspendWithObjectMonitorWaitReentryPartSecond extends SuspendWithObjectMonitorWaitBase {
|
||||
|
||||
@Override
|
||||
public int run(int timeMax, PrintStream out) {
|
||||
return doWork3(timeMax, out);
|
||||
}
|
||||
|
||||
// Suspend on the re-entry path of wait.
|
||||
public int doWork3(int timeMax, PrintStream out) {
|
||||
SuspendWithObjectMonitorWaitWorker waiter; // waiter thread
|
||||
SuspendWithObjectMonitorWaitWorker resumer; // resumer thread
|
||||
|
||||
System.out.println("Test 3: About to execute for " + timeMax + " seconds.");
|
||||
|
||||
long start_time = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
|
||||
count++;
|
||||
testState = TS_INIT; // starting the test loop
|
||||
|
||||
// launch the waiter thread
|
||||
waiter = launchWaiter(100);
|
||||
|
||||
// launch the resumer thread
|
||||
resumer = launchResumer(waiter);
|
||||
|
||||
checkTestState(TS_RESUMER_RUNNING);
|
||||
|
||||
// The waiter thread was synchronized on threadLock before it
|
||||
// set TS_WAITER_RUNNING and notified barrierLaunch above so
|
||||
// we cannot enter threadLock until the waiter thread calls
|
||||
// threadLock.wait().
|
||||
synchronized (threadLock) {
|
||||
// notify waiter thread so it can try to reenter threadLock
|
||||
testState = TS_READY_TO_NOTIFY;
|
||||
threadLock.notify();
|
||||
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (Exception e) {}
|
||||
|
||||
// wait for the waiter thread to block
|
||||
logDebug("before contended enter wait");
|
||||
int retCode = wait4ContendedEnter(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI GetThreadState: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("done contended enter wait");
|
||||
|
||||
checkTestState(TS_READY_TO_NOTIFY);
|
||||
testState = TS_CALL_SUSPEND;
|
||||
logDebug("before suspend thread");
|
||||
retCode = suspendThread(waiter);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI SuspendThread: "
|
||||
+ "retCode=" + retCode);
|
||||
}
|
||||
logDebug("suspended thread");
|
||||
|
||||
//
|
||||
// At this point, all of the child threads are running
|
||||
// and we can get to meat of the test:
|
||||
//
|
||||
// - suspended threadLock waiter (trying to reenter)
|
||||
// - a blocked threadLock enter in the resumer thread while the
|
||||
// threadLock is held by the main thread.
|
||||
// - resumption of the waiter thread
|
||||
// - a threadLock enter in the freshly resumed waiter thread
|
||||
//
|
||||
barrierResumerNotify();
|
||||
try {
|
||||
// Delay for 1-second while holding the threadLock to force the
|
||||
// resumer thread to block on entering the threadLock.
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
shutDown(waiter ,resumer);
|
||||
checkTestState(TS_WAITER_DONE);
|
||||
}
|
||||
|
||||
System.out.println("Executed " + count + " loops in " + timeMax +
|
||||
" seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2026, 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.
|
||||
*/
|
||||
|
||||
public class SuspendWithObjectMonitorWaitWorker extends Thread {
|
||||
private SuspendWithObjectMonitorWaitWorker target; // target for resume operation
|
||||
private final long waitTimeout;
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker(String name, long waitTimeout) {
|
||||
super(name);
|
||||
this.waitTimeout = waitTimeout;
|
||||
}
|
||||
|
||||
public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) {
|
||||
super(name);
|
||||
this.target = target;
|
||||
this.waitTimeout = 0;
|
||||
}
|
||||
|
||||
native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr);
|
||||
|
||||
public void run() {
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("thread running");
|
||||
|
||||
//
|
||||
// Launch the waiter thread:
|
||||
// - grab the threadLock
|
||||
// - threadLock.wait()
|
||||
// - releases threadLock
|
||||
//
|
||||
if (getName().equals("waiter")) {
|
||||
// grab threadLock before we tell main we are running
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock");
|
||||
synchronized(SuspendWithObjectMonitorWaitBase.threadLock) {
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock");
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_INIT);
|
||||
|
||||
synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) {
|
||||
// tell main we are running
|
||||
SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_RUNNING;
|
||||
SuspendWithObjectMonitorWaitBase.barrierLaunch.notify();
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("before wait");
|
||||
|
||||
// TS_READY_TO_NOTIFY is set after the main thread has
|
||||
// entered threadLock so a spurious wakeup can't get the
|
||||
// waiter thread out of this threadLock.wait(0) call:
|
||||
while (SuspendWithObjectMonitorWaitBase.testState <= SuspendWithObjectMonitorWaitBase.TS_READY_TO_NOTIFY) {
|
||||
try {
|
||||
SuspendWithObjectMonitorWaitBase.threadLock.wait(waitTimeout);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("after wait");
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME);
|
||||
SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_DONE;
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock");
|
||||
}
|
||||
}
|
||||
//
|
||||
// Launch the resumer thread:
|
||||
// - tries to grab the threadLock (should not block with doWork1!)
|
||||
// - grabs threadLock
|
||||
// - resumes the waiter thread
|
||||
// - releases threadLock
|
||||
//
|
||||
else if (getName().equals("resumer")) {
|
||||
synchronized(SuspendWithObjectMonitorWaitBase.barrierResumer) {
|
||||
synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) {
|
||||
// tell main we are running
|
||||
SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_RESUMER_RUNNING;
|
||||
SuspendWithObjectMonitorWaitBase.barrierLaunch.notify();
|
||||
}
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("thread waiting");
|
||||
while (SuspendWithObjectMonitorWaitBase.testState != SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME) {
|
||||
try {
|
||||
// wait for main to tell us when to continue
|
||||
SuspendWithObjectMonitorWaitBase.barrierResumer.wait(0);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock");
|
||||
synchronized(SuspendWithObjectMonitorWaitBase.threadLock) {
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock");
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME);
|
||||
SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME;
|
||||
|
||||
// resume the waiter thread so waiter.join() can work
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("before resume thread");
|
||||
int retCode = resumeThread(target);
|
||||
if (retCode != 0) {
|
||||
throw new RuntimeException("error in JVMTI ResumeThread: " +
|
||||
"retCode=" + retCode);
|
||||
}
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("resumed thread");
|
||||
|
||||
SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, 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
|
||||
@ -36,7 +36,7 @@ static jvmtiEnv* jvmti = nullptr;
|
||||
} while (0)
|
||||
|
||||
JNIEXPORT int JNICALL
|
||||
Java_SuspendWithObjectMonitorWait_suspendThread(JNIEnv *jni, jclass cls, jthread thr) {
|
||||
Java_SuspendWithObjectMonitorWaitBase_suspendThread(JNIEnv *jni, jclass cls, jthread thr) {
|
||||
return jvmti->SuspendThread(thr);
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ Java_SuspendWithObjectMonitorWaitWorker_resumeThread(JNIEnv *jni, jclass cls, jt
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_SuspendWithObjectMonitorWait_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) {
|
||||
Java_SuspendWithObjectMonitorWaitBase_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) {
|
||||
jvmtiError err;
|
||||
jint thread_state;
|
||||
do {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user