// The real <stdatomic.h> checks for the availability of C++'s <atomic> and // uses that instead if present. // We want to test the C interfaces, so we instead include // <bits/stdatomic.h> directly. // This doesn't entirely work because gtest also (transitively) pulls in <atomic>. // It's not clear there's a good fix for this, // other than switching to a non-C++ unit test framework for bionic. // Bionic has <stdatomic.h>, which includes <bits/stdatomic.h>, but GCC and // Clang only provide <stdatomic.h>, so only include <bits/stdatomic.h> when it // exists. That is, include <bits/stdatomic.h> for bionic but not for glibc. #if __has_include(<bits/stdatomic.h>) #include <bits/stdatomic.h> #else #include <stdatomic.h> #endif
TEST(stdatomic, init) { // ATOMIC_VAR_INIT has been removed from C23, // but is still in POSIX 2024. // Even if it is removed from there, // we should probably keep it indefinitely for source compatibility. // libc++'s <atomic> (which we can't entirely avoid: see above) // marks the macro deprecated, // so we need to silence that. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-pragma"
atomic_int v = ATOMIC_VAR_INIT(123);
ASSERT_EQ(123, atomic_load(&v)); #pragma clang diagnostic pop
struct three_atomics {
atomic_uint_least32_t x; char a[123]; // Everything in different cache lines, // increase chance of compiler getting alignment wrong.
atomic_uint_least32_t y; char b[4013];
atomic_uint_least32_t z;
};
atomic_bool read_enough(false);
// Very simple acquire/release memory ordering smoke test. staticvoid* writer(void* arg) {
three_atomics* a = reinterpret_cast<three_atomics*>(arg); for (uint_least32_t i = 0; i <= BIG; i+=2) {
atomic_store_explicit(&a->x, i, memory_order_relaxed);
atomic_store_explicit(&a->z, i, memory_order_relaxed);
atomic_store_explicit(&a->y, i, memory_order_release);
// Force stores to be visible in spite of being overwritten below. asmvolatile("" ::: "memory");
atomic_store_explicit(&a->x, i+1, memory_order_relaxed);
atomic_store_explicit(&a->z, i+1, memory_order_relaxed);
atomic_store_explicit(&a->y, i+1, memory_order_release); if (i >= BIG - 1000 && !atomic_load(&read_enough)) { // Give reader a chance to catch up, at the expense of making the test // less effective.
usleep(1000);
}
} return nullptr;
}
staticvoid* reader(void* arg) {
three_atomics* a = reinterpret_cast<three_atomics*>(arg);
uint_least32_t xval = 0, yval = 0, zval = 0;
size_t repeat = 0;
size_t repeat_limit = 1000; while (yval != BIG + 1) {
yval = atomic_load_explicit(&a->y, memory_order_acquire);
zval = atomic_load_explicit(&a->z, memory_order_relaxed);
xval = atomic_load_explicit(&a->x, memory_order_relaxed); // If we see a given value of y, the immediately preceding // stores to z and x, or later ones, should also be visible. if (zval < yval) { // Cant just ASSERT, since we are in a non-void function.
ADD_FAILURE() << "acquire-release ordering violation: "
<< zval << " < " << yval << ", " << xval << "\n"; return nullptr; // Only report once.
} if (xval < yval) { // Cant just ASSERT, since we are in a non-void function.
ADD_FAILURE() << "acquire-release ordering violation: "
<< xval << " < " << yval << ", " << zval << "\n"; return nullptr; // Only report once.
} if (repeat < repeat_limit) {
++repeat;
} elseif (!atomic_load_explicit(&read_enough, memory_order_relaxed)) {
atomic_store_explicit(&read_enough, true, memory_order_relaxed);
}
} // The following assertion is not technically guaranteed to hold. // But if it fails to hold, this test was useless, and we have a // serious scheduling issue that we should probably know about.
EXPECT_EQ(repeat, repeat_limit); return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.