Add test_atomic_c11 (C11 version of test_atomic_cxx). NFC (#26930)
diff --git a/test/core/test_atomic_c11.c b/test/core/test_atomic_c11.c
new file mode 100644
index 0000000..6ee24ff
--- /dev/null
+++ b/test/core/test_atomic_c11.c
@@ -0,0 +1,157 @@
+// Copyright 2026 The Emscripten Authors. All rights reserved.
+// Emscripten is available under two separate licenses, the MIT license and the
+// University of Illinois/NCSA Open Source License. Both these licenses can be
+// found in the LICENSE file.
+
+//------------------------------------------------------------------------------
+// test C11 atomics
+//
+// This is a trasnliteration of the test_atomic_cxx.cpp test into C11 atomics.
+//------------------------------------------------------------------------------
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#define TEST(TYPE, UNSIGNED_TYPE, MASK0, MASK1, MASK2) \
+ { \
+ const TYPE numMemoryOrders = 6; \
+ memory_order memoryOrder[] = { \
+ memory_order_relaxed, \
+ memory_order_consume, \
+ memory_order_acquire, \
+ memory_order_release, \
+ memory_order_acq_rel, \
+ memory_order_seq_cst, \
+ }; \
+ \
+ _Atomic TYPE atomicDog = 5; \
+ printf("is_lock_free: %s\n", \
+ atomic_is_lock_free(&atomicDog) ? "true" : "false"); \
+ printf("value: %lld\n", (long long)(TYPE)atomicDog); \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ atomic_store_explicit(&atomicDog, i, memoryOrder[i]); \
+ printf("store/load %lld: %lld\n", \
+ (long long)i, \
+ (long long)atomic_load_explicit(&atomicDog, memoryOrder[i])); \
+ } \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE old = atomic_exchange_explicit(&atomicDog, i, memoryOrder[i]); \
+ printf("exchange %lld: old=%lld new=%lld\n", \
+ (long long)i, \
+ (long long)old, \
+ (long long)(TYPE)atomicDog); \
+ } \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE expected = i; \
+ bool success = atomic_compare_exchange_weak_explicit( \
+ &atomicDog, &expected, i + 1, memoryOrder[i], memoryOrder[i]); \
+ i = expected; \
+ printf("compare_exchange_weak %lld: success = %s\n", \
+ (long long)i, \
+ success ? "true" : "false"); \
+ } \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE expected = i; \
+ bool success = atomic_compare_exchange_strong_explicit( \
+ &atomicDog, &expected, i + 1, memoryOrder[i], memoryOrder[i]); \
+ i = expected; \
+ printf("compare_exchange_strong %lld: success = %s\n", \
+ (long long)i, \
+ success ? "true" : "false"); \
+ } \
+ \
+ atomicDog = MASK2; \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE old = atomic_fetch_add_explicit(&atomicDog, 1, memoryOrder[i]); \
+ printf("fetch_add %lld: old=%llx new=%llx\n", \
+ (long long)i, \
+ (long long)old, \
+ (long long)(TYPE)atomicDog); \
+ } \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE old = atomic_fetch_sub_explicit(&atomicDog, 1, memoryOrder[i]); \
+ printf("fetch_sub %lld: old=%llx new=%llx\n", \
+ (long long)i, \
+ (long long)old, \
+ (long long)(TYPE)atomicDog); \
+ } \
+ \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ atomic_store_explicit(&atomicDog, MASK0, memoryOrder[i]); \
+ TYPE old = \
+ atomic_fetch_and_explicit(&atomicDog, (TYPE)1 << i, memoryOrder[i]); \
+ printf("fetch_and %lld: old=%llx, new=%llx\n", \
+ (long long)i, \
+ (unsigned long long)(UNSIGNED_TYPE)old, \
+ (unsigned long long)(UNSIGNED_TYPE)atomicDog); \
+ } \
+ \
+ atomicDog = 0; \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE old = \
+ atomic_fetch_or_explicit(&atomicDog, (TYPE)1 << i, memoryOrder[i]); \
+ printf("fetch_or %lld: old=%llx, new=%llx\n", \
+ (long long)i, \
+ (unsigned long long)(UNSIGNED_TYPE)old, \
+ (unsigned long long)(UNSIGNED_TYPE)atomicDog); \
+ } \
+ \
+ atomicDog = 0; \
+ for (TYPE i = 0; i < numMemoryOrders; i++) { \
+ TYPE old = \
+ atomic_fetch_xor_explicit(&atomicDog, (TYPE)1 << i, memoryOrder[i]); \
+ printf("fetch_xor %lld: old=%llx, new=%llx\n", \
+ (long long)i, \
+ (unsigned long long)(UNSIGNED_TYPE)old, \
+ (unsigned long long)(UNSIGNED_TYPE)atomicDog); \
+ } \
+ \
+ atomicDog = 0; \
+ atomicDog++; \
+ printf("operator++: %lld\n", (long long)(TYPE)(atomicDog)); \
+ atomicDog--; \
+ printf("operator--: %lld\n", (long long)(TYPE)(atomicDog)); \
+ \
+ atomicDog += 10; \
+ printf("operator+=: %lld\n", (long long)(TYPE)(atomicDog)); \
+ atomicDog -= 5; \
+ printf("operator-=: %lld\n", (long long)(TYPE)(atomicDog)); \
+ atomicDog |= MASK0; \
+ printf("operator|=: %llx\n", \
+ (unsigned long long)(UNSIGNED_TYPE)(atomicDog)); \
+ atomicDog &= MASK1; \
+ printf("operator&=: %llx\n", \
+ (unsigned long long)(UNSIGNED_TYPE)(atomicDog)); \
+ atomicDog ^= MASK2; \
+ printf("operator^=: %llx\n", \
+ (unsigned long long)(UNSIGNED_TYPE)(atomicDog)); \
+ }
+
+int main() {
+ // test 8, 16, 32 and 64-bit data types
+ printf("\n8 bits\n\n");
+ TEST(char, unsigned char, 0xFF, 0xF0, 0x0F);
+ printf("\n16 bits\n\n");
+ TEST(short, unsigned short, 0xFFFF, 0xF0F0, 0x0F0F);
+ printf("\n32 bits\n\n");
+ TEST(int, unsigned int, 0xFFFFFFFF, 0xF0F0F0F0, 0x0F0F0F0F);
+ printf("\n64 bits\n\n");
+ TEST(long long, unsigned long long, 0xFFFFFFFFFFFFFFFF,
+ 0xF0F0F0F0F0F0F0F0, 0x0F0F0F0F0F0F0F0F);
+
+ // test atomic_flag (should also have memory_orders, but probably doesn't
+ // matter to find the missing atomic functions)
+ atomic_flag af = ATOMIC_FLAG_INIT;
+ atomic_flag_clear(&af);
+ bool b = atomic_flag_test_and_set(&af);
+ printf("atomic_flag: %s\n", b ? "true" : "false");
+
+ printf("done.\n");
+ return 0;
+}
diff --git a/test/core/test_atomic_cxx.cpp b/test/core/test_atomic_cxx.cpp
index 1f64e1a..0c30fa6 100644
--- a/test/core/test_atomic_cxx.cpp
+++ b/test/core/test_atomic_cxx.cpp
@@ -28,8 +28,8 @@
// test atomic<int>
std::atomic<dog> atomicDog(5);
- printf("atomic<int>.is_lock_free(): %s\n", atomicDog.is_lock_free() ? "true" : "false");
- printf("atomic<int> value: %lld\n", (long long)TYPE(atomicDog));
+ printf("is_lock_free: %s\n", atomicDog.is_lock_free() ? "true" : "false");
+ printf("value: %lld\n", (long long)TYPE(atomicDog));
// test store/load
for (TYPE i = 0; i < numMemoryOrders; i++) {
diff --git a/test/core/test_atomic_cxx.out b/test/core/test_atomic_cxx.out
index c74c742..db264c9 100644
--- a/test/core/test_atomic_cxx.out
+++ b/test/core/test_atomic_cxx.out
@@ -1,8 +1,8 @@
8 bits
-atomic<int>.is_lock_free(): true
-atomic<int> value: 5
+is_lock_free: true
+value: 5
store/load 0: 0
store/load 1: 1
store/load 2: 2
@@ -57,8 +57,8 @@
16 bits
-atomic<int>.is_lock_free(): true
-atomic<int> value: 5
+is_lock_free: true
+value: 5
store/load 0: 0
store/load 1: 1
store/load 2: 2
@@ -113,8 +113,8 @@
32 bits
-atomic<int>.is_lock_free(): true
-atomic<int> value: 5
+is_lock_free: true
+value: 5
store/load 0: 0
store/load 1: 1
store/load 2: 2
@@ -169,8 +169,8 @@
64 bits
-atomic<int>.is_lock_free(): true
-atomic<int> value: 5
+is_lock_free: true
+value: 5
store/load 0: 0
store/load 1: 1
store/load 2: 2
diff --git a/test/test_core.py b/test/test_core.py
index 7d52e6f..7533ba5 100644
--- a/test/test_core.py
+++ b/test/test_core.py
@@ -6275,6 +6275,14 @@
self.do_core_test('test_atomic.c')
@also_with_pthreads
+ def test_atomic_c11(self):
+ if '-pthread' in self.cflags and self.is_wasm2js():
+ self.skipTest('atomics support missing')
+ # Re-use the out file from the C++ atomic test since they should have
+ # identical output.
+ self.do_runf('core/test_atomic_c11.c', read_file(test_file('core/test_atomic_cxx.out')))
+
+ @also_with_pthreads
def test_atomic_cxx(self):
if '-pthread' in self.cflags and self.is_wasm2js():
self.skipTest('atomics support missing')