blob: 4c6dd4c8c8d10d2a7aecbdb2726f0511f6de2aac [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/unittest/unittest.h>
#include <ktl/atomic.h>
#include <ktl/enforce.h>
// Verify that 16-byte atomics are unconditionally supported, regardless of
// compiler intrinsic support. But on RISC-V they are not available at all.
// See atomic.cc for details on 'polyfills'.
#ifndef HAVE_ATOMIC_128
#error "<ktl/atomic.h> should define HAVE_ATOMIC_128 to 0 or 1"
#endif
#if HAVE_ATOMIC_128
namespace {
constexpr unsigned __int128 kValue =
(static_cast<unsigned __int128>(0x0123456789abcdef) << 64) + 0xfedcba9876543210;
bool Load16Test() {
BEGIN_TEST;
{
ktl::atomic<unsigned __int128> v = 0u;
EXPECT_EQ(v.load(), 0u);
}
{
ktl::atomic<unsigned __int128> v = kValue;
EXPECT_EQ(v.load(), kValue);
}
END_TEST;
}
bool Store16Test() {
BEGIN_TEST;
ktl::atomic<unsigned __int128> v = 0u;
EXPECT_EQ(v.load(), 0u);
v.store(kValue);
EXPECT_EQ(v.load(), kValue);
END_TEST;
}
bool CompareExchange16Test() {
BEGIN_TEST;
{
// Comparison fails.
ktl::atomic<unsigned __int128> v = kValue;
unsigned __int128 expected = kValue - 1;
EXPECT_FALSE(v.compare_exchange_strong(expected, 0u));
EXPECT_EQ(expected, kValue);
EXPECT_EQ(v.load(), kValue);
}
{
// Comparison succeeds.
ktl::atomic<unsigned __int128> v = kValue;
unsigned __int128 expected = kValue;
constexpr unsigned __int128 kDesired = 0xaaaabbbbccccdddd;
EXPECT_TRUE(v.compare_exchange_strong(expected, kDesired));
EXPECT_EQ(expected, kValue);
EXPECT_EQ(v.load(), kDesired);
}
END_TEST;
}
// Most of atomic_ref's tests are in stdcompat, along the rest of the polyfill
// library. We test __int128 specifically in the kernel unit tests, since
// __int128 is unconditionally available in the kernel environment.
bool KtlAtomicRef128Test() {
BEGIN_TEST;
unsigned __int128 i = 0;
ktl::atomic_ref<unsigned __int128> int_ref(i);
int_ref.store(1);
EXPECT_EQ(1u, int_ref.load());
unsigned __int128 expected = 1u;
EXPECT_EQ(true, int_ref.compare_exchange_strong(expected, 2u));
EXPECT_EQ(2u, int_ref.load());
expected = 0;
EXPECT_EQ(false, int_ref.compare_exchange_strong(expected, 3u));
EXPECT_EQ(expected, 2u);
int_ref.store(kValue, ktl::memory_order_release);
EXPECT_EQ(int_ref.load(ktl::memory_order_acquire), kValue);
int_ref.store(kValue + 1, ktl::memory_order_release);
EXPECT_EQ(int_ref.load(ktl::memory_order_acquire), kValue + 1);
// TODO(https://fxbug.dev/42123828): gcc __int128 is not considered is_lock_free, even though it
// generates lock-free code for load/store/compare_exchange.
#ifdef __clang__
EXPECT_EQ(true, int_ref.is_lock_free());
#endif
END_TEST;
}
} // namespace
UNITTEST_START_TESTCASE(libc_atomic_tests)
UNITTEST("load_16", Load16Test)
UNITTEST("store_16", Store16Test)
UNITTEST("compare_exchange_16", CompareExchange16Test)
UNITTEST("ktl::atomic_ref_128", KtlAtomicRef128Test)
UNITTEST_END_TESTCASE(libc_atomic_tests, "atomic", "atomic operations test")
#endif // HAVE_ATOMIC_128