On AIX, this test sometimes fails with error `Assertion failed: y == true`. The test assumes `compare_exchange_weak` should succeed on a single call, however according to the standard: > A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory referred to by expected and ptr are equal, it may return false and store back to expected the same memory contents that were originally there. This spurious failure enables implementation of compare-and-exchange on a broader class of machines, e.g., load-locked store-conditional machines. A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop. [atomics.ref.ops]/27
231 lines
8.1 KiB
C++
231 lines
8.1 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
|
// XFAIL: !has-64-bit-atomics
|
|
// XFAIL: !has-1024-bit-atomics
|
|
|
|
// MSVC warning C4310: cast truncates constant value
|
|
// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4310
|
|
|
|
// bool compare_exchange_weak(T&, T, memory_order, memory_order) const noexcept;
|
|
// bool compare_exchange_weak(T&, T, memory_order = memory_order::seq_cst) const noexcept;
|
|
|
|
#include <atomic>
|
|
#include <concepts>
|
|
#include <cassert>
|
|
#include <type_traits>
|
|
|
|
#include "atomic_helpers.h"
|
|
#include "test_helper.h"
|
|
#include "test_macros.h"
|
|
|
|
template <typename T>
|
|
struct TestCompareExchangeWeak {
|
|
void operator()() const {
|
|
{
|
|
alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
|
|
std::atomic_ref<T> const a(x);
|
|
|
|
T t(T(1));
|
|
while (!a.compare_exchange_weak(t, T(2))) {
|
|
}
|
|
assert(a == T(2));
|
|
assert(t == T(1));
|
|
std::same_as<bool> decltype(auto) y = a.compare_exchange_weak(t, T(3));
|
|
assert(y == false);
|
|
assert(a == T(2));
|
|
assert(t == T(2));
|
|
|
|
ASSERT_NOEXCEPT(a.compare_exchange_weak(t, T(2)));
|
|
}
|
|
{
|
|
alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
|
|
std::atomic_ref<T> const a(x);
|
|
|
|
T t(T(1));
|
|
while (!a.compare_exchange_weak(t, T(2), std::memory_order_seq_cst)) {
|
|
}
|
|
assert(a == T(2));
|
|
assert(t == T(1));
|
|
std::same_as<bool> decltype(auto) y = a.compare_exchange_weak(t, T(3), std::memory_order_seq_cst);
|
|
assert(y == false);
|
|
assert(a == T(2));
|
|
assert(t == T(2));
|
|
|
|
ASSERT_NOEXCEPT(a.compare_exchange_weak(t, T(2), std::memory_order_seq_cst));
|
|
}
|
|
{
|
|
T x(T(1));
|
|
std::atomic_ref<T> const a(x);
|
|
|
|
T t(T(1));
|
|
while (!a.compare_exchange_weak(t, T(2), std::memory_order_release, std::memory_order_relaxed)) {
|
|
}
|
|
assert(a == T(2));
|
|
assert(t == T(1));
|
|
std::same_as<bool> decltype(auto) y =
|
|
a.compare_exchange_weak(t, T(3), std::memory_order_release, std::memory_order_relaxed);
|
|
assert(y == false);
|
|
assert(a == T(2));
|
|
assert(t == T(2));
|
|
|
|
ASSERT_NOEXCEPT(a.compare_exchange_weak(t, T(2), std::memory_order_release, std::memory_order_relaxed));
|
|
}
|
|
|
|
// success memory_order::release
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release, std::memory_order::relaxed)) {
|
|
}
|
|
};
|
|
|
|
auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); };
|
|
test_acquire_release<T>(store, load);
|
|
|
|
auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release)) {
|
|
}
|
|
};
|
|
test_acquire_release<T>(store_one_arg, load);
|
|
}
|
|
|
|
// success memory_order::acquire
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
|
|
auto load = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::acquire, std::memory_order::relaxed)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_acquire_release<T>(store, load);
|
|
|
|
auto load_one_arg = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::acquire)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_acquire_release<T>(store, load_one_arg);
|
|
}
|
|
|
|
// success memory_order::acq_rel
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel, std::memory_order::relaxed)) {
|
|
}
|
|
};
|
|
auto load = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel, std::memory_order::relaxed)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_acquire_release<T>(store, load);
|
|
|
|
auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel)) {
|
|
}
|
|
};
|
|
auto load_one_arg = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_acquire_release<T>(store_one_arg, load_one_arg);
|
|
}
|
|
|
|
// success memory_order::seq_cst
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst, std::memory_order::relaxed)) {
|
|
}
|
|
};
|
|
auto load = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst, std::memory_order::relaxed)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_seq_cst<T>(store, load);
|
|
|
|
auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) {
|
|
// could fail spuriously, so put it in a loop
|
|
while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst)) {
|
|
}
|
|
};
|
|
auto load_one_arg = [](std::atomic_ref<T> const& x) {
|
|
auto val = x.load(std::memory_order::relaxed);
|
|
while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst)) {
|
|
}
|
|
return val;
|
|
};
|
|
test_seq_cst<T>(store_one_arg, load_one_arg);
|
|
}
|
|
|
|
// failure memory_order::acquire
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
|
|
auto load = [](std::atomic_ref<T> const& x) {
|
|
auto result = x.load(std::memory_order::relaxed);
|
|
T unexpected(T(255));
|
|
bool r =
|
|
x.compare_exchange_weak(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::acquire);
|
|
assert(!r);
|
|
return result;
|
|
};
|
|
test_acquire_release<T>(store, load);
|
|
|
|
auto load_one_arg = [](std::atomic_ref<T> const& x) {
|
|
auto result = x.load(std::memory_order::relaxed);
|
|
T unexpected(T(255));
|
|
bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order::acquire);
|
|
assert(!r);
|
|
return result;
|
|
};
|
|
test_acquire_release<T>(store, load_one_arg);
|
|
|
|
// acq_rel replaced by acquire
|
|
auto load_one_arg_acq_rel = [](std::atomic_ref<T> const& x) {
|
|
auto result = x.load(std::memory_order::relaxed);
|
|
T unexpected(T(255));
|
|
bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order::acq_rel);
|
|
assert(!r);
|
|
return result;
|
|
};
|
|
test_acquire_release<T>(store, load_one_arg_acq_rel);
|
|
}
|
|
|
|
// failure memory_order::seq_cst
|
|
{
|
|
auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::seq_cst); };
|
|
auto load = [](std::atomic_ref<T> const& x) {
|
|
auto result = x.load(std::memory_order::relaxed);
|
|
T unexpected(T(255));
|
|
bool r =
|
|
x.compare_exchange_weak(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::seq_cst);
|
|
assert(!r);
|
|
return result;
|
|
};
|
|
test_seq_cst<T>(store, load);
|
|
}
|
|
}
|
|
};
|
|
|
|
int main(int, char**) {
|
|
TestEachAtomicType<TestCompareExchangeWeak>()();
|
|
return 0;
|
|
}
|