8355646: Optimize ObjectMonitor::exit

Reviewed-by: pchilanomate, coleenp
This commit is contained in:
Fredrik Bredberg 2025-05-12 08:16:15 +00:00
parent a3afc9f7ce
commit 39a28ffe4e

View File

@ -1507,7 +1507,34 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) {
#endif
for (;;) {
assert(has_owner(current), "invariant");
// If there is a successor we should release the lock as soon as
// possible, so that the successor can acquire the lock. If there is
// no successor, we might need to wake up a waiting thread.
if (!has_successor()) {
ObjectWaiter* w = Atomic::load(&_entry_list);
if (w != nullptr) {
// Other threads are blocked trying to acquire the lock and
// there is no successor, so it appears that an heir-
// presumptive (successor) must be made ready. Since threads
// are woken up in FIFO order, we need to find the tail of the
// entry_list.
w = entry_list_tail(current);
// I'd like to write: guarantee (w->_thread != current).
// But in practice an exiting thread may find itself on the entry_list.
// Let's say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and
// then calls exit(). Exit release the lock by setting O._owner to null.
// Let's say T1 then stalls. T2 acquires O and calls O.notify(). The
// notify() operation moves T1 from O's waitset to O's entry_list. T2 then
// release the lock "O". T1 resumes immediately after the ST of null into
// _owner, above. T1 notices that the entry_list is populated, so it
// reacquires the lock and then finds itself on the entry_list.
// Given all that, we have to tolerate the circumstance where "w" is
// associated with current.
assert(w->TState == ObjectWaiter::TS_ENTER, "invariant");
exit_epilog(current, w);
return;
}
}
// Drop the lock.
// release semantics: prior loads and stores from within the critical section
@ -1547,11 +1574,9 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) {
return;
}
// Other threads are blocked trying to acquire the lock and there
// is no successor, so it appears that an heir-presumptive
// (successor) must be made ready. Only the current lock owner can
// detach threads from the entry_list, therefore we need to
// reacquire the lock. If we fail to reacquire the lock the
// Only the current lock owner can manipulate the entry_list
// (except for pushing new threads to the head), therefore we need
// to reacquire the lock. If we fail to reacquire the lock the
// responsibility for ensuring succession falls to the new owner.
if (try_lock(current) != TryLockResult::Success) {
@ -1561,27 +1586,6 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) {
}
guarantee(has_owner(current), "invariant");
ObjectWaiter* w = nullptr;
w = Atomic::load(&_entry_list);
if (w != nullptr) {
w = entry_list_tail(current);
// I'd like to write: guarantee (w->_thread != current).
// But in practice an exiting thread may find itself on the entry_list.
// Let's say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and
// then calls exit(). Exit release the lock by setting O._owner to null.
// Let's say T1 then stalls. T2 acquires O and calls O.notify(). The
// notify() operation moves T1 from O's waitset to O's entry_list. T2 then
// release the lock "O". T1 resumes immediately after the ST of null into
// _owner, above. T1 notices that the entry_list is populated, so it
// reacquires the lock and then finds itself on the entry_list.
// Given all that, we have to tolerate the circumstance where "w" is
// associated with current.
assert(w->TState == ObjectWaiter::TS_ENTER, "invariant");
exit_epilog(current, w);
return;
}
}
}