[libcxx-commits] [libcxx] [libc++] Add an internal asymmetric thread fence helper (PR #231475)
Nikita Grivin via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 14 02:52:30 PDT 2026
https://github.com/MindSpectre updated https://github.com/llvm/llvm-project/pull/231475
>From c7f84382b9ffa44721284a2d99f0aded00160d02 Mon Sep 17 00:00:00 2001
From: Nikita Grivin <neuronspectrelin at gmail.com>
Date: Tue, 13 Oct 2026 15:12:33 +0300
Subject: [PATCH] [libc++] Add an internal asymmetric thread fence helper
Add std::__asymmetric_thread_fence_light/heavy(memory_order), shaped after
the Concurrency TS 2 (P1202) asymmetric_thread_fence_{light,heavy}, as a
private helper for <hazard_pointer> and <rcu>. Nothing public is added.
RFC: https://discourse.llvm.org/t/libc-asymmetric-fences/92444
A light fence and a heavy fence order memory like two seq_cst fences. Both
ignore their memory_order argument for now and always behave as seq_cst.
As suggested in the RFC, whether the platform supports the asymmetric
barrier is decided in the dylib, not in the headers:
- The light fence is inline. It reads the exported flag
__asymmetric_thread_fence_is_asymmetric and is a compiler barrier when the
flag is true, or a seq_cst fence otherwise. On Windows it is always a
compiler barrier.
- The heavy fence is the exported __asymmetric_thread_fence_heavy_impl.
- On Linux, the first call registers membarrier(2) PRIVATE_EXPEDITED once,
in a function-local static, and only then sets the flag. Every heavy
fence after that is a membarrier, so a light fence that sees the flag
always pairs with a process-wide barrier.
- Without membarrier, the flag stays false and both sides are seq_cst
fences. There is no mprotect() fallback yet (left as a TODO).
- On Windows the heavy fence is FlushProcessWriteBuffers; elsewhere it is
a seq_cst fence.
- Where the dylib lacks the symbols (availability, LLVM 24), both fences
are plain inline seq_cst fences.
The tests cover:
- a store-buffering litmus test, which fails if the heavy fence is replaced
by a plain fence;
- the flag's state when 8 threads issue the first heavy fence at once;
- fork();
- fences issued from destructors at static destruction and thread exit.
---
libcxx/include/CMakeLists.txt | 1 +
.../__atomic/asymmetric_thread_fence.h | 74 ++++++++++++
libcxx/include/__configuration/availability.h | 5 +
libcxx/include/module.modulemap.in | 35 +++---
libcxx/lib/abi/CHANGELOG.TXT | 10 ++
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...xxabi.v1.stable.noexceptions.nonew.abilist | 2 +
libcxx/src/CMakeLists.txt | 1 +
libcxx/src/asymmetric_thread_fence.cpp | 105 ++++++++++++++++++
.../asymmetric_thread_fence/flag.pass.cpp | 71 ++++++++++++
.../asymmetric_thread_fence/fork.pass.cpp | 40 +++++++
.../header.compile.pass.cpp | 24 ++++
.../asymmetric_thread_fence/store_buffering.h | 76 +++++++++++++
.../store_buffering.pass.cpp | 55 +++++++++
21 files changed, 498 insertions(+), 17 deletions(-)
create mode 100644 libcxx/include/__atomic/asymmetric_thread_fence.h
create mode 100644 libcxx/src/asymmetric_thread_fence.cpp
create mode 100644 libcxx/test/libcxx/atomics/asymmetric_thread_fence/flag.pass.cpp
create mode 100644 libcxx/test/libcxx/atomics/asymmetric_thread_fence/fork.pass.cpp
create mode 100644 libcxx/test/libcxx/atomics/asymmetric_thread_fence/header.compile.pass.cpp
create mode 100644 libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.h
create mode 100644 libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 9af3d00f36f7e6..8b59bb2a7b3521 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -210,6 +210,7 @@ set(files
__algorithm/upper_bound.h
__assert
__atomic/aliases.h
+ __atomic/asymmetric_thread_fence.h
__atomic/atomic.h
__atomic/atomic_flag.h
__atomic/atomic_init.h
diff --git a/libcxx/include/__atomic/asymmetric_thread_fence.h b/libcxx/include/__atomic/asymmetric_thread_fence.h
new file mode 100644
index 00000000000000..3217a8b563ed47
--- /dev/null
+++ b/libcxx/include/__atomic/asymmetric_thread_fence.h
@@ -0,0 +1,74 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ATOMIC_ASYMMETRIC_THREAD_FENCE_H
+#define _LIBCPP___ATOMIC_ASYMMETRIC_THREAD_FENCE_H
+
+#include <__atomic/atomic.h>
+#include <__atomic/fence.h>
+#include <__atomic/memory_order.h>
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_HAS_THREADS
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// Asymmetric thread fences, shaped after Concurrency TS 2 (P1202) asymmetric_thread_fence_{light,heavy}.
+// A light fence and a heavy fence order memory as if both were atomic_thread_fence(memory_order_seq_cst);
+// two light fences order nothing with respect to each other. The light fence is meant for hot paths and
+// the heavy fence for rare ones.
+//
+// Whether the heavy fence is a process-wide barrier (which lets the light fence be a compiler barrier) is
+// decided by the built library, not by these headers: __asymmetric_thread_fence_is_asymmetric becomes true
+// at most once, inside the first heavy fence, and only if every heavy fence from then on is such a barrier.
+// On Windows the library always provides one. Both fences currently ignore their memory_order argument and
+// behave as seq_cst.
+//
+// Where the dylib may lack these symbols (vendor availability annotations, e.g. Apple back-deployment), both
+// fences are plain inline fences. A heavy fence compiled that way can never become a process-wide barrier, so
+// on such platforms the library must keep the flag false (and the light fence a full fence) forever; otherwise
+// a light fence from newer code could pair with an old inline heavy fence and order nothing.
+
+# if _LIBCPP_AVAILABILITY_HAS_ASYMMETRIC_THREAD_FENCE
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+extern _LIBCPP_EXPORTED_FROM_ABI atomic<bool> __asymmetric_thread_fence_is_asymmetric;
+_LIBCPP_EXPORTED_FROM_ABI void __asymmetric_thread_fence_heavy_impl(memory_order) _NOEXCEPT;
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+# endif
+
+_LIBCPP_HIDE_FROM_ABI inline void __asymmetric_thread_fence_light([[__maybe_unused__]] memory_order __order) _NOEXCEPT {
+# if !_LIBCPP_AVAILABILITY_HAS_ASYMMETRIC_THREAD_FENCE
+ std::atomic_thread_fence(memory_order_seq_cst);
+# elif defined(_WIN32)
+ std::atomic_signal_fence(memory_order_seq_cst);
+# else
+ if (__asymmetric_thread_fence_is_asymmetric.load(memory_order_relaxed))
+ std::atomic_signal_fence(memory_order_seq_cst);
+ else
+ std::atomic_thread_fence(memory_order_seq_cst);
+# endif
+}
+
+_LIBCPP_HIDE_FROM_ABI inline void __asymmetric_thread_fence_heavy([[__maybe_unused__]] memory_order __order) _NOEXCEPT {
+# if !_LIBCPP_AVAILABILITY_HAS_ASYMMETRIC_THREAD_FENCE
+ std::atomic_thread_fence(memory_order_seq_cst);
+# else
+ std::__asymmetric_thread_fence_heavy_impl(__order);
+# endif
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_THREADS
+
+#endif // _LIBCPP___ATOMIC_ASYMMETRIC_THREAD_FENCE_H
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 75d911438e4af1..04a8f84de15bdd 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -284,6 +284,11 @@
#define _LIBCPP_AVAILABILITY_HAS_NEW_SYNC _LIBCPP_INTRODUCED_IN_LLVM_22
#define _LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_INTRODUCED_IN_LLVM_22_ATTRIBUTE
+// This controls whether the asymmetric thread fence entry points (an internal helper used by
+// <hazard_pointer> and <rcu>) are available in the dylib.
+#define _LIBCPP_AVAILABILITY_HAS_ASYMMETRIC_THREAD_FENCE _LIBCPP_INTRODUCED_IN_LLVM_24
+// No attribute, since the symbols are only referenced under the macro above
+
// This controls whether `std::__hash_memory` is available in the dylib, which
// is used for some `std::hash` specializations.
#define _LIBCPP_AVAILABILITY_HAS_HASH_MEMORY _LIBCPP_INTRODUCED_IN_LLVM_21
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 2f6640f8469a5b..c7cc47801755bd 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -879,27 +879,28 @@ module std {
}
module atomic {
- module aliases { header "__atomic/aliases.h" }
- module atomic_flag { header "__atomic/atomic_flag.h" }
- module atomic_init { header "__atomic/atomic_init.h" }
- module atomic_lock_free { header "__atomic/atomic_lock_free.h" }
- module atomic_ref { header "__atomic/atomic_ref.h" }
- module atomic_sync { header "__atomic/atomic_sync.h" }
- module atomic_sync_timed { header "__atomic/atomic_sync_timed.h" }
- module atomic_waitable_traits { header "__atomic/atomic_waitable_traits.h" }
+ module aliases { header "__atomic/aliases.h" }
+ module asymmetric_thread_fence { header "__atomic/asymmetric_thread_fence.h" }
+ module atomic_flag { header "__atomic/atomic_flag.h" }
+ module atomic_init { header "__atomic/atomic_init.h" }
+ module atomic_lock_free { header "__atomic/atomic_lock_free.h" }
+ module atomic_ref { header "__atomic/atomic_ref.h" }
+ module atomic_sync { header "__atomic/atomic_sync.h" }
+ module atomic_sync_timed { header "__atomic/atomic_sync_timed.h" }
+ module atomic_waitable_traits { header "__atomic/atomic_waitable_traits.h" }
module atomic {
header "__atomic/atomic.h"
export std.atomic.atomic_base // most of std::atomic methods are defined there
}
- module check_memory_order { header "__atomic/check_memory_order.h" }
- module clear_padding { header "__atomic/clear_padding.h" }
- module contention_t { header "__atomic/contention_t.h" }
- module fence { header "__atomic/fence.h" }
- module floating_point_helper { header "__atomic/floating_point_helper.h" }
- module is_always_lock_free { header "__atomic/is_always_lock_free.h" }
- module kill_dependency { header "__atomic/kill_dependency.h" }
- module memory_order { header "__atomic/memory_order.h" }
- module to_gcc_order { header "__atomic/to_gcc_order.h" }
+ module check_memory_order { header "__atomic/check_memory_order.h" }
+ module clear_padding { header "__atomic/clear_padding.h" }
+ module contention_t { header "__atomic/contention_t.h" }
+ module fence { header "__atomic/fence.h" }
+ module floating_point_helper { header "__atomic/floating_point_helper.h" }
+ module is_always_lock_free { header "__atomic/is_always_lock_free.h" }
+ module kill_dependency { header "__atomic/kill_dependency.h" }
+ module memory_order { header "__atomic/memory_order.h" }
+ module to_gcc_order { header "__atomic/to_gcc_order.h" }
module support {
header "__atomic/support.h"
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index 85d2e596ed51f4..2c224e372ab3f2 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -16,6 +16,16 @@ New entries should be added directly below the "Version" header.
Version 24.0
------------
+* [libc++] Add an internal asymmetric thread fence helper
+
+ The heavy side of the asymmetric thread fence lives in the dylib, together with the flag telling the
+ inline light side whether the heavy side is a process-wide barrier.
+
+ All platforms
+ -------------
+ Symbol added: _ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE
+ Symbol added: _ZNSt3__139__asymmetric_thread_fence_is_asymmetricE
+
* [libc++] Remove locale::id::__next_id from the ABI
`__next_id` is only ever used inside the dylib, so we can avoid making it public.
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74e0af974e4538..2abf481dab05ae 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1592,6 +1592,8 @@
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_y', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvxy', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__13cinE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 1b0cd758bfcf36..2f7b504902396c 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1229,6 +1229,8 @@
{'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk133__atomic_wait_native_with_timeoutILj4EEEvPKvS2_y', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERi', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk139__asymmetric_thread_fence_is_asymmetricE', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt6__ndk139__atomic_wait_global_table_with_timeoutEPKviy', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 88, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index c61db56dc2b904..96690d1dacd5f3 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -578,6 +578,8 @@
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__atomic_notify_one_global_tableEPKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__139__atomic_wait_global_table_with_timeoutEPKviy', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__13cinE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7663aa877a096f..9d8be9262cab07 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -578,6 +578,8 @@
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__atomic_notify_one_global_tableEPKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvlm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__13cinE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 00b5949f843acd..970f7f6faa9df5 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1582,6 +1582,8 @@
{'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__13cinE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 536614e0ccfa64..42e10f68b91781 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1229,6 +1229,8 @@
{'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk133__atomic_wait_native_with_timeoutILm4EEEvPKvS2_m', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk139__asymmetric_thread_fence_is_asymmetricE', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt6__ndk139__atomic_wait_global_table_with_timeoutEPKvim', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 168, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index d655541acdc8d5..7b8717434c11ad 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1242,6 +1242,8 @@
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_m', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvlm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 168, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 69c32bd5be8775..8c007928a2b844 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1240,6 +1240,8 @@
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__133__atomic_wait_native_with_timeoutILm4EEEvPKvS2_m', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvim', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 168, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index b6850b2ec80938..2adc47f5fc001c 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1211,6 +1211,8 @@
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__133__atomic_wait_native_with_timeoutILm4EEEvPKvS2_m', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__136__asymmetric_thread_fence_heavy_implENS_12memory_orderE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__139__asymmetric_thread_fence_is_asymmetricE', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvim', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 168, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 99e6bfb5692f52..b5d9f30883ae22 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -68,6 +68,7 @@ set(LIBCXX_SOURCES
if (LIBCXX_ENABLE_THREADS)
list(APPEND LIBCXX_SOURCES
+ asymmetric_thread_fence.cpp
atomic.cpp
barrier.cpp
condition_variable_destructor.cpp
diff --git a/libcxx/src/asymmetric_thread_fence.cpp b/libcxx/src/asymmetric_thread_fence.cpp
new file mode 100644
index 00000000000000..a2e4dc0509619e
--- /dev/null
+++ b/libcxx/src/asymmetric_thread_fence.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <__atomic/fence.h>
+#include <__atomic/memory_order.h>
+#include <__config>
+#include <__verbose_abort>
+
+#if defined(__linux__)
+# include <cerrno>
+# include <sys/syscall.h>
+# include <unistd.h>
+#elif defined(_WIN32)
+# define WIN32_LEAN_AND_MEAN
+# define VC_EXTRA_LEAN
+# include <windows.h>
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+// See <__atomic/asymmetric_thread_fence.h> for the protocol. The flag may only become true once every heavy
+// fence that can still run is a process-wide barrier, and it never becomes false again.
+#if defined(_WIN32)
+constinit atomic<bool> __asymmetric_thread_fence_is_asymmetric{true};
+#else
+constinit atomic<bool> __asymmetric_thread_fence_is_asymmetric{false};
+#endif
+
+#if defined(__linux__)
+
+namespace {
+
+# if defined(SYS_membarrier)
+// membarrier(2) commands. The values are kernel ABI; spelling them here keeps the build independent of the
+// UAPI headers it was configured with.
+constexpr int __membarrier_cmd_query = 0;
+constexpr int __membarrier_cmd_private_expedited = 1 << 3;
+constexpr int __membarrier_cmd_register_private_expedited = 1 << 4;
+
+long __membarrier(int __cmd) noexcept { return ::syscall(SYS_membarrier, __cmd, 0, 0); }
+
+bool __enable_membarrier() noexcept {
+ long __commands = __membarrier(__membarrier_cmd_query);
+ return __commands != -1 && (__commands & __membarrier_cmd_private_expedited) != 0 &&
+ __membarrier(__membarrier_cmd_register_private_expedited) == 0;
+}
+# else
+bool __enable_membarrier() noexcept { return false; }
+# endif
+
+} // namespace
+
+// TODO: add an mprotect()-based fallback (as folly does; possibly x86-only, since on AArch64 the TLB
+// invalidation is broadcast in hardware and does not interrupt the other CPUs) for kernels without
+// membarrier(2) PRIVATE_EXPEDITED. This is private to the library, so it can change without ABI impact.
+void __asymmetric_thread_fence_heavy_impl([[maybe_unused]] memory_order __order) noexcept {
+ // Decided once, before any heavy fence could be a plain fence while the flag reads true: concurrent first
+ // calls wait for this initialization, and the flag is only ever set here.
+ static const bool __asymmetric = [] {
+ bool __ok = __enable_membarrier();
+ if (__ok)
+ __asymmetric_thread_fence_is_asymmetric.store(true, memory_order_relaxed);
+ return __ok;
+ }();
+ if (!__asymmetric) {
+ std::atomic_thread_fence(memory_order_seq_cst);
+ return;
+ }
+# if defined(SYS_membarrier)
+ if (__membarrier(__membarrier_cmd_private_expedited) == 0)
+ return;
+ // Light fences may already rely on this barrier, so never fall back to a plain fence. The registration
+ // survives fork() and only exec() clears it (which also reloads this library), so EPERM is not expected;
+ // should the registration ever be lost anyway, register again.
+ if (errno == EPERM && __membarrier(__membarrier_cmd_register_private_expedited) == 0 &&
+ __membarrier(__membarrier_cmd_private_expedited) == 0)
+ return;
+# endif
+ __libcpp_verbose_abort("membarrier(2) failed after the asymmetric thread fence was enabled");
+}
+
+#else
+
+void __asymmetric_thread_fence_heavy_impl([[maybe_unused]] memory_order __order) noexcept {
+# if defined(_WIN32)
+ ::FlushProcessWriteBuffers();
+# else
+ // TODO: process-wide barriers on more platforms (FreeBSD 14.1+ has membarrier(2)). Not on platforms with
+ // vendor availability annotations (Apple): code back-deployed to a dylib without these symbols issues inline
+ // plain heavy fences, so the flag must stay false there (see <__atomic/asymmetric_thread_fence.h>).
+ std::atomic_thread_fence(memory_order_seq_cst);
+# endif
+}
+
+#endif
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/atomics/asymmetric_thread_fence/flag.pass.cpp b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/flag.pass.cpp
new file mode 100644
index 00000000000000..4a44139d1fb54d
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/flag.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+// UNSUPPORTED: no-threads
+// REQUIRES: linux || windows
+
+// __asymmetric_thread_fence_is_asymmetric is always true on Windows. On Linux it starts false, only a heavy
+// fence can set it, and after the first heavy fence it tells whether the kernel provides membarrier(2)
+// PRIVATE_EXPEDITED -- also when that first heavy fence is issued by many threads at once.
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <atomic>
+#include <cassert>
+#include <thread>
+#include <vector>
+
+#include "make_test_thread.h"
+
+#if defined(__linux__)
+# include <sys/syscall.h>
+# include <unistd.h>
+#endif
+
+#if !defined(_WIN32)
+static bool kernel_has_private_expedited_membarrier() {
+# if defined(__linux__) && defined(SYS_membarrier)
+ long commands = syscall(SYS_membarrier, /* MEMBARRIER_CMD_QUERY */ 0, 0, 0);
+ return commands != -1 && (commands & /* MEMBARRIER_CMD_PRIVATE_EXPEDITED */ (1 << 3)) != 0;
+# else
+ return false;
+# endif
+}
+#endif
+
+int main(int, char**) {
+#if defined(_WIN32)
+ assert(std::__asymmetric_thread_fence_is_asymmetric.load());
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+ assert(std::__asymmetric_thread_fence_is_asymmetric.load());
+#else
+ assert(!std::__asymmetric_thread_fence_is_asymmetric.load());
+ std::__asymmetric_thread_fence_light(std::memory_order_seq_cst);
+ assert(!std::__asymmetric_thread_fence_is_asymmetric.load());
+
+ // The very first heavy fence comes from 8 threads at once.
+ std::atomic<bool> go(false);
+ std::vector<std::thread> threads;
+ for (int i = 0; i != 8; ++i)
+ threads.push_back(support::make_test_thread([&go] {
+ while (!go.load())
+ std::this_thread::yield();
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+ std::__asymmetric_thread_fence_light(std::memory_order_seq_cst);
+ }));
+ go.store(true);
+ for (std::thread& t : threads)
+ t.join();
+
+ const bool expected = kernel_has_private_expedited_membarrier();
+ assert(std::__asymmetric_thread_fence_is_asymmetric.load() == expected);
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+ assert(std::__asymmetric_thread_fence_is_asymmetric.load() == expected);
+#endif
+ return 0;
+}
diff --git a/libcxx/test/libcxx/atomics/asymmetric_thread_fence/fork.pass.cpp b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/fork.pass.cpp
new file mode 100644
index 00000000000000..b237831b7e01d4
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/fork.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: windows
+
+// After the parent has issued a heavy fence (which enables the asymmetric path where the system supports
+// it), a forked child can still use both fences, and they still order memory.
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <atomic>
+#include <cassert>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "store_buffering.h"
+
+int main(int, char**) {
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+
+ pid_t pid = fork();
+ assert(pid != -1);
+ if (pid == 0) {
+ std::__asymmetric_thread_fence_light(std::memory_order_seq_cst);
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+ check_store_buffering(light, heavy, std::memory_order_seq_cst, 20000);
+ _exit(0);
+ }
+
+ int status = 0;
+ assert(waitpid(pid, &status, 0) == pid);
+ assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/atomics/asymmetric_thread_fence/header.compile.pass.cpp b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/header.compile.pass.cpp
new file mode 100644
index 00000000000000..d37e037feac3c9
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/header.compile.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+// UNSUPPORTED: no-threads
+
+// <__atomic/asymmetric_thread_fence.h> is self-contained and warning-free in every language mode
+// (the test suite compiles with -Wunused-parameter -Werror and _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER,
+// so an unused memory_order parameter without [[maybe_unused]] fails here).
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <__atomic/memory_order.h>
+
+void test(std::memory_order order) {
+ static_assert(noexcept(std::__asymmetric_thread_fence_light(order)), "");
+ static_assert(noexcept(std::__asymmetric_thread_fence_heavy(order)), "");
+ std::__asymmetric_thread_fence_light(order);
+ std::__asymmetric_thread_fence_heavy(order);
+}
diff --git a/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.h b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.h
new file mode 100644
index 00000000000000..314ca470f15cb7
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.h
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_LIBCXX_ATOMICS_ASYMMETRIC_THREAD_FENCE_STORE_BUFFERING_H
+#define TEST_LIBCXX_ATOMICS_ASYMMETRIC_THREAD_FENCE_STORE_BUFFERING_H
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <atomic>
+#include <cassert>
+#include <thread>
+
+#include "make_test_thread.h"
+
+// A two-thread spin barrier: neither thread leaves arrive_and_wait() before both have entered it.
+class SpinBarrier {
+public:
+ SpinBarrier() : arrived_(0), phase_(0) {}
+
+ void arrive_and_wait() {
+ int phase = phase_.load(std::memory_order_acquire);
+ if (arrived_.fetch_add(1, std::memory_order_acq_rel) == 1) {
+ arrived_.store(0, std::memory_order_relaxed);
+ phase_.store(phase + 1, std::memory_order_release);
+ } else {
+ while (phase_.load(std::memory_order_acquire) == phase)
+ std::this_thread::yield();
+ }
+ }
+
+private:
+ std::atomic<int> arrived_;
+ std::atomic<int> phase_;
+};
+
+using Fence = void (*)(std::memory_order);
+
+inline void light(std::memory_order order) { std::__asymmetric_thread_fence_light(order); }
+inline void heavy(std::memory_order order) { std::__asymmetric_thread_fence_heavy(order); }
+
+// Store buffering (Dekker): `x = 1; fence_a; r1 = y` races with `y = 1; fence_b; r2 = x`.
+// If the two fences order memory like two seq_cst fences, r1 == 0 && r2 == 0 is impossible.
+inline void check_store_buffering(Fence fence_a, Fence fence_b, std::memory_order order, int rounds) {
+ std::atomic<int> x(0);
+ std::atomic<int> y(0);
+ int r2 = -1;
+ SpinBarrier barrier;
+ std::thread other = support::make_test_thread([&] {
+ for (int i = 0; i != rounds; ++i) {
+ barrier.arrive_and_wait(); // start of round
+ y.store(1, std::memory_order_relaxed);
+ fence_b(order);
+ r2 = x.load(std::memory_order_relaxed);
+ barrier.arrive_and_wait(); // r2 published
+ barrier.arrive_and_wait(); // x and y reset
+ }
+ });
+ for (int i = 0; i != rounds; ++i) {
+ barrier.arrive_and_wait();
+ x.store(1, std::memory_order_relaxed);
+ fence_a(order);
+ int r1 = y.load(std::memory_order_relaxed);
+ barrier.arrive_and_wait();
+ assert(!(r1 == 0 && r2 == 0));
+ x.store(0, std::memory_order_relaxed);
+ y.store(0, std::memory_order_relaxed);
+ barrier.arrive_and_wait();
+ }
+ other.join();
+}
+
+#endif // TEST_LIBCXX_ATOMICS_ASYMMETRIC_THREAD_FENCE_STORE_BUFFERING_H
diff --git a/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.pass.cpp b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.pass.cpp
new file mode 100644
index 00000000000000..6f958ee3729ecb
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/asymmetric_thread_fence/store_buffering.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++11
+// UNSUPPORTED: no-threads
+
+// A light/heavy (or heavy/heavy) pair of asymmetric fences orders memory like a pair of seq_cst fences.
+// Two light fences give no guarantee and are not tested.
+
+#include <__atomic/asymmetric_thread_fence.h>
+#include <atomic>
+#include <thread>
+
+#include "make_test_thread.h"
+#include "store_buffering.h"
+
+// Fences are issued from destructors during static destruction and thread exit (where e.g. a hazard
+// pointer domain is torn down); they must neither crash nor deadlock there.
+struct FenceInDestructor {
+ ~FenceInDestructor() {
+ std::__asymmetric_thread_fence_light(std::memory_order_seq_cst);
+ std::__asymmetric_thread_fence_heavy(std::memory_order_seq_cst);
+ }
+};
+
+FenceInDestructor fence_at_exit;
+
+int main(int, char**) {
+ check_store_buffering(light, heavy, std::memory_order_seq_cst, 100000);
+ check_store_buffering(heavy, light, std::memory_order_seq_cst, 100000);
+ check_store_buffering(heavy, heavy, std::memory_order_seq_cst, 20000);
+
+ // libc++ ignores the order and always fences as seq_cst (P1202 would allow weaker orders to do less).
+ const std::memory_order orders[] = {
+ std::memory_order_relaxed,
+ std::memory_order_consume,
+ std::memory_order_acquire,
+ std::memory_order_release,
+ std::memory_order_acq_rel,
+ std::memory_order_seq_cst};
+ for (std::memory_order order : orders)
+ check_store_buffering(light, heavy, order, 5000);
+
+ support::make_test_thread([] {
+ thread_local FenceInDestructor fence_at_thread_exit;
+ (void)&fence_at_thread_exit;
+ }).join();
+
+ return 0;
+}
More information about the libcxx-commits
mailing list