From 336894857bfc9f610da55e6180dd7b668bf67752 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Sun, 11 Jan 2026 20:37:04 +0000 Subject: [PATCH] 8374878: Add Atomic::compare_set Reviewed-by: kbarrett, stefank --- src/hotspot/share/gc/shared/oopStorage.cpp | 2 +- src/hotspot/share/gc/shared/pretouchTask.cpp | 2 +- src/hotspot/share/gc/shared/taskqueue.hpp | 6 ++-- .../share/gc/shared/taskqueue.inline.hpp | 7 ++--- src/hotspot/share/runtime/atomic.hpp | 13 +++++++++ .../utilities/concurrentHashTable.inline.hpp | 4 +-- .../share/utilities/waitBarrier_generic.cpp | 6 ++-- test/hotspot/gtest/runtime/test_atomic.cpp | 29 +++++++++++++++++++ 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index a1cc3ffa553..21e63f6fc32 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) { // then someone else has made such a claim and the deferred update has not // yet been processed and will include our change, so we don't need to do // anything further. - if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) { + if (_deferred_updates_next.compare_set(nullptr, this)) { // Successfully claimed. Push, with self-loop for end-of-list. Block* head = owner->_deferred_updates.load_relaxed(); while (true) { diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp index c999c98ea99..58a3a2693ed 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.cpp +++ b/src/hotspot/share/gc/shared/pretouchTask.cpp @@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) { char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1)); if (cur_start >= cur_end) { break; - } else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) { + } else if (_cur_addr.compare_set(cur_start, cur_end)) { os::pretouch_memory(cur_start, cur_end, _page_size); } // Else attempt to claim chunk failed, so try again. } diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index 3a751852ab6..4334773a4e9 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -183,8 +183,8 @@ protected: _age.store_relaxed(new_age); } - Age cmpxchg_age(Age old_age, Age new_age) { - return _age.compare_exchange(old_age, new_age); + bool par_set_age(Age old_age, Age new_age) { + return _age.compare_set(old_age, new_age); } idx_t age_top_relaxed() const { @@ -345,7 +345,7 @@ protected: using TaskQueueSuper::age_relaxed; using TaskQueueSuper::set_age_relaxed; - using TaskQueueSuper::cmpxchg_age; + using TaskQueueSuper::par_set_age; using TaskQueueSuper::age_top_relaxed; using TaskQueueSuper::increment_index; diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index f115d94740b..55851495a5f 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -170,8 +170,7 @@ bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) { if (localBot == oldAge.top()) { // No competing pop_global has yet incremented "top"; we'll try to // install new_age, thus claiming the element. - Age tempAge = cmpxchg_age(oldAge, newAge); - if (tempAge == oldAge) { + if (par_set_age(oldAge, newAge)) { // We win. assert_not_underflow(localBot, age_top_relaxed()); TASKQUEUE_STATS_ONLY(stats.record_pop_slow()); @@ -283,12 +282,12 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g idx_t new_top = increment_index(oldAge.top()); idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0); Age newAge(new_top, new_tag); - Age resAge = cmpxchg_age(oldAge, newAge); + bool result = par_set_age(oldAge, newAge); // Note that using "bottom" here might fail, since a pop_local might // have decremented it. assert_not_underflow(localBot, newAge.top()); - return resAge == oldAge ? PopResult::Success : PopResult::Contended; + return result ? PopResult::Success : PopResult::Contended; } inline int randomParkAndMiller(int *seed0) { diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index 02e9f82cfb6..f708e9c18ca 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -75,6 +75,7 @@ // v.release_store(x) -> void // v.release_store_fence(x) -> void // v.compare_exchange(x, y [, o]) -> T +// v.compare_set(x, y [, o]) -> bool // v.exchange(x [, o]) -> T // // (2) All atomic types are default constructible. @@ -267,6 +268,11 @@ public: return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order); } + bool compare_set(T compare_value, T new_value, + atomic_memory_order order = memory_order_conservative) { + return compare_exchange(compare_value, new_value, order) == compare_value; + } + T exchange(T new_value, atomic_memory_order order = memory_order_conservative) { return AtomicAccess::xchg(this->value_ptr(), new_value, order); @@ -479,6 +485,13 @@ public: order)); } + bool compare_set(T compare_value, T new_value, + atomic_memory_order order = memory_order_conservative) { + return _value.compare_set(decay(compare_value), + decay(new_value), + order); + } + T exchange(T new_value, atomic_memory_order order = memory_order_conservative) { return recover(_value.exchange(decay(new_value), order)); } diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 31b451ba38a..62d2dd29dab 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -157,7 +157,7 @@ inline bool ConcurrentHashTable:: if (is_locked()) { return false; } - if (_first.compare_exchange(expect, node) == expect) { + if (_first.compare_set(expect, node)) { return true; } return false; @@ -172,7 +172,7 @@ inline bool ConcurrentHashTable:: } // We will expect a clean first pointer. Node* tmp = first(); - if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) { + if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) { return true; } return false; diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index b268b10c757..0892feab699 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp @@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(0, waiters); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Successfully disarmed. break; } @@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(tag, waiters + 1); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Success! Proceed to wait. break; } @@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { tag, waiters); int64_t new_state = encode(tag, waiters - 1); - if (_state.compare_exchange(state, new_state) == state) { + if (_state.compare_set(state, new_state)) { // Success! break; } diff --git a/test/hotspot/gtest/runtime/test_atomic.cpp b/test/hotspot/gtest/runtime/test_atomic.cpp index b37c14d41a7..753dde0ca57 100644 --- a/test/hotspot/gtest/runtime/test_atomic.cpp +++ b/test/hotspot/gtest/runtime/test_atomic.cpp @@ -162,6 +162,35 @@ TEST_VM(AtomicIntegerTest, cmpxchg_int64) { Support().test(); } +template +struct AtomicIntegerCmpsetTestSupport { + Atomic _test_value; + + AtomicIntegerCmpsetTestSupport() : _test_value{} {} + + void test() { + T zero = 0; + T five = 5; + T ten = 10; + _test_value.store_relaxed(zero); + EXPECT_FALSE(_test_value.compare_set(five, ten)); + EXPECT_EQ(zero, _test_value.load_relaxed()); + EXPECT_TRUE(_test_value.compare_set(zero, ten)); + EXPECT_EQ(ten, _test_value.load_relaxed()); + } +}; + +TEST_VM(AtomicIntegerTest, cmpset_int32) { + using Support = AtomicIntegerCmpsetTestSupport; + Support().test(); +} + +TEST_VM(AtomicIntegerTest, cmpset_int64) { + // Check if 64-bit atomics are available on the machine. + using Support = AtomicIntegerCmpsetTestSupport; + Support().test(); +} + struct AtomicXchgAndCmpxchg1ByteStressSupport { char _default_val; int _base;