Path: blob/main/contrib/llvm-project/libc/src/__support/CPP/atomic.h
213799 views
//===-- A simple equivalent of std::atomic ----------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H9#define LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H1011#include "src/__support/CPP/type_traits/has_unique_object_representations.h"12#include "src/__support/macros/attributes.h"13#include "src/__support/macros/config.h"14#include "src/__support/macros/properties/architectures.h"1516#include "type_traits.h"1718namespace LIBC_NAMESPACE_DECL {19namespace cpp {2021enum class MemoryOrder : int {22RELAXED = __ATOMIC_RELAXED,23CONSUME = __ATOMIC_CONSUME,24ACQUIRE = __ATOMIC_ACQUIRE,25RELEASE = __ATOMIC_RELEASE,26ACQ_REL = __ATOMIC_ACQ_REL,27SEQ_CST = __ATOMIC_SEQ_CST28};2930// These are a clang extension, see the clang documentation for more31// information:32// https://clang.llvm.org/docs/LanguageExtensions.html#scoped-atomic-builtins.33enum class MemoryScope : int {34#if defined(__MEMORY_SCOPE_SYSTEM) && defined(__MEMORY_SCOPE_DEVICE)35SYSTEM = __MEMORY_SCOPE_SYSTEM,36DEVICE = __MEMORY_SCOPE_DEVICE,37#else38SYSTEM = 0,39DEVICE = 0,40#endif41};4243namespace impl {44LIBC_INLINE constexpr int order(MemoryOrder mem_ord) {45return static_cast<int>(mem_ord);46}4748LIBC_INLINE constexpr int scope(MemoryScope mem_scope) {49return static_cast<int>(mem_scope);50}5152template <class T> LIBC_INLINE T *addressof(T &ref) {53return __builtin_addressof(ref);54}5556LIBC_INLINE constexpr int infer_failure_order(MemoryOrder mem_ord) {57if (mem_ord == MemoryOrder::RELEASE)58return order(MemoryOrder::RELAXED);59if (mem_ord == MemoryOrder::ACQ_REL)60return order(MemoryOrder::ACQUIRE);61return order(mem_ord);62}63} // namespace impl6465template <typename T> struct Atomic {66static_assert(is_trivially_copyable_v<T> && is_copy_constructible_v<T> &&67is_move_constructible_v<T> && is_copy_assignable_v<T> &&68is_move_assignable_v<T>,69"atomic<T> requires T to be trivially copyable, copy "70"constructible, move constructible, copy assignable, "71"and move assignable.");7273static_assert(cpp::has_unique_object_representations_v<T>,74"atomic<T> in libc only support types whose values has unique "75"object representations.");7677private:78// type conversion helper to avoid long c++ style casts7980// Require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to81// at least their size to be potentially used lock-free.82LIBC_INLINE_VAR static constexpr size_t MIN_ALIGNMENT =83(sizeof(T) & (sizeof(T) - 1)) || (sizeof(T) > 16) ? 0 : sizeof(T);8485LIBC_INLINE_VAR static constexpr size_t ALIGNMENT = alignof(T) > MIN_ALIGNMENT86? alignof(T)87: MIN_ALIGNMENT;8889public:90using value_type = T;9192// We keep the internal value public so that it can be addressable.93// This is useful in places like the Linux futex operations where94// we need pointers to the memory of the atomic values. Load and store95// operations should be performed using the atomic methods however.96alignas(ALIGNMENT) value_type val;9798LIBC_INLINE constexpr Atomic() = default;99100// Initializes the value without using atomic operations.101LIBC_INLINE constexpr Atomic(value_type v) : val(v) {}102103LIBC_INLINE Atomic(const Atomic &) = delete;104LIBC_INLINE Atomic &operator=(const Atomic &) = delete;105106// Atomic load.107LIBC_INLINE operator T() { return load(); }108109LIBC_INLINE T110load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST,111[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {112T res;113#if __has_builtin(__scoped_atomic_load)114__scoped_atomic_load(impl::addressof(val), impl::addressof(res),115impl::order(mem_ord), impl::scope(mem_scope));116#else117__atomic_load(impl::addressof(val), impl::addressof(res),118impl::order(mem_ord));119#endif120return res;121}122123// Atomic store.124LIBC_INLINE T operator=(T rhs) {125store(rhs);126return rhs;127}128129LIBC_INLINE void130store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,131[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {132#if __has_builtin(__scoped_atomic_store)133__scoped_atomic_store(impl::addressof(val), impl::addressof(rhs),134impl::order(mem_ord), impl::scope(mem_scope));135#else136__atomic_store(impl::addressof(val), impl::addressof(rhs),137impl::order(mem_ord));138#endif139}140141// Atomic compare exchange142LIBC_INLINE bool compare_exchange_strong(143T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,144[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {145return __atomic_compare_exchange(146impl::addressof(val), impl::addressof(expected),147impl::addressof(desired), false, impl::order(mem_ord),148impl::infer_failure_order(mem_ord));149}150151// Atomic compare exchange (separate success and failure memory orders)152LIBC_INLINE bool compare_exchange_strong(153T &expected, T desired, MemoryOrder success_order,154MemoryOrder failure_order,155[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {156return __atomic_compare_exchange(157impl::addressof(val), impl::addressof(expected),158impl::addressof(desired), false, impl::order(success_order),159impl::order(failure_order));160}161162// Atomic compare exchange (weak version)163LIBC_INLINE bool compare_exchange_weak(164T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,165[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {166return __atomic_compare_exchange(167impl::addressof(val), impl::addressof(expected),168impl::addressof(desired), true, impl::order(mem_ord),169impl::infer_failure_order(mem_ord));170}171172// Atomic compare exchange (weak version with separate success and failure173// memory orders)174LIBC_INLINE bool compare_exchange_weak(175T &expected, T desired, MemoryOrder success_order,176MemoryOrder failure_order,177[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {178return __atomic_compare_exchange(179impl::addressof(val), impl::addressof(expected),180impl::addressof(desired), true, impl::order(success_order),181impl::order(failure_order));182}183184LIBC_INLINE T185exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,186[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {187T ret;188#if __has_builtin(__scoped_atomic_exchange)189__scoped_atomic_exchange(impl::addressof(val), impl::addressof(desired),190impl::addressof(ret), impl::order(mem_ord),191impl::scope(mem_scope));192#else193__atomic_exchange(impl::addressof(val), impl::addressof(desired),194impl::addressof(ret), impl::order(mem_ord));195#endif196return ret;197}198199LIBC_INLINE T200fetch_add(T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,201[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {202static_assert(cpp::is_integral_v<T>, "T must be an integral type.");203#if __has_builtin(__scoped_atomic_fetch_add)204return __scoped_atomic_fetch_add(impl::addressof(val), increment,205impl::order(mem_ord),206impl::scope(mem_scope));207#else208return __atomic_fetch_add(impl::addressof(val), increment,209impl::order(mem_ord));210#endif211}212213LIBC_INLINE T214fetch_or(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,215[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {216static_assert(cpp::is_integral_v<T>, "T must be an integral type.");217#if __has_builtin(__scoped_atomic_fetch_or)218return __scoped_atomic_fetch_or(impl::addressof(val), mask,219impl::order(mem_ord),220impl::scope(mem_scope));221#else222return __atomic_fetch_or(impl::addressof(val), mask, impl::order(mem_ord));223#endif224}225226LIBC_INLINE T227fetch_and(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,228[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {229static_assert(cpp::is_integral_v<T>, "T must be an integral type.");230#if __has_builtin(__scoped_atomic_fetch_and)231return __scoped_atomic_fetch_and(impl::addressof(val), mask,232impl::order(mem_ord),233impl::scope(mem_scope));234#else235return __atomic_fetch_and(impl::addressof(val), mask, impl::order(mem_ord));236#endif237}238239LIBC_INLINE T240fetch_sub(T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,241[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {242static_assert(cpp::is_integral_v<T>, "T must be an integral type.");243#if __has_builtin(__scoped_atomic_fetch_sub)244return __scoped_atomic_fetch_sub(impl::addressof(val), decrement,245impl::order(mem_ord),246impl::scope(mem_scope));247#else248return __atomic_fetch_sub(impl::addressof(val), decrement,249impl::order(mem_ord));250#endif251}252253// Set the value without using an atomic operation. This is useful254// in initializing atomic values without a constructor.255LIBC_INLINE void set(T rhs) { val = rhs; }256};257258template <typename T> struct AtomicRef {259static_assert(is_trivially_copyable_v<T> && is_copy_constructible_v<T> &&260is_move_constructible_v<T> && is_copy_assignable_v<T> &&261is_move_assignable_v<T>,262"AtomicRef<T> requires T to be trivially copyable, copy "263"constructible, move constructible, copy assignable, "264"and move assignable.");265266static_assert(cpp::has_unique_object_representations_v<T>,267"AtomicRef<T> only supports types with unique object "268"representations.");269270private:271T *ptr;272273public:274// Constructor from T reference275LIBC_INLINE explicit constexpr AtomicRef(T &obj) : ptr(&obj) {}276277// Non-standard Implicit conversion from T*278LIBC_INLINE constexpr AtomicRef(T *obj) : ptr(obj) {}279280LIBC_INLINE AtomicRef(const AtomicRef &) = default;281LIBC_INLINE AtomicRef &operator=(const AtomicRef &) = default;282283// Atomic load284LIBC_INLINE operator T() const { return load(); }285286LIBC_INLINE T287load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST,288[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {289T res;290#if __has_builtin(__scoped_atomic_load)291__scoped_atomic_load(ptr, &res, impl::order(mem_ord),292impl::scope(mem_scope));293#else294__atomic_load(ptr, &res, impl::order(mem_ord));295#endif296return res;297}298299// Atomic store300LIBC_INLINE T operator=(T rhs) const {301store(rhs);302return rhs;303}304305LIBC_INLINE void306store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,307[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {308#if __has_builtin(__scoped_atomic_store)309__scoped_atomic_store(ptr, &rhs, impl::order(mem_ord),310impl::scope(mem_scope));311#else312__atomic_store(ptr, &rhs, impl::order(mem_ord));313#endif314}315316// Atomic compare exchange (strong)317LIBC_INLINE bool compare_exchange_strong(318T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,319[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {320return __atomic_compare_exchange(ptr, &expected, &desired, false,321impl::order(mem_ord),322impl::infer_failure_order(mem_ord));323}324325// Atomic compare exchange (strong, separate success/failure memory orders)326LIBC_INLINE bool compare_exchange_strong(327T &expected, T desired, MemoryOrder success_order,328MemoryOrder failure_order,329[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {330return __atomic_compare_exchange(ptr, &expected, &desired, false,331impl::order(success_order),332impl::order(failure_order));333}334335// Atomic exchange336LIBC_INLINE T337exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,338[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {339T ret;340#if __has_builtin(__scoped_atomic_exchange)341__scoped_atomic_exchange(ptr, &desired, &ret, impl::order(mem_ord),342impl::scope(mem_scope));343#else344__atomic_exchange(ptr, &desired, &ret, impl::order(mem_ord));345#endif346return ret;347}348349LIBC_INLINE T fetch_add(350T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,351[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {352static_assert(cpp::is_integral_v<T>, "T must be an integral type.");353#if __has_builtin(__scoped_atomic_fetch_add)354return __scoped_atomic_fetch_add(ptr, increment, impl::order(mem_ord),355impl::scope(mem_scope));356#else357return __atomic_fetch_add(ptr, increment, impl::order(mem_ord));358#endif359}360361LIBC_INLINE T362fetch_or(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,363[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {364static_assert(cpp::is_integral_v<T>, "T must be an integral type.");365#if __has_builtin(__scoped_atomic_fetch_or)366return __scoped_atomic_fetch_or(ptr, mask, impl::order(mem_ord),367impl::scope(mem_scope));368#else369return __atomic_fetch_or(ptr, mask, impl::order(mem_ord));370#endif371}372373LIBC_INLINE T fetch_and(374T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,375[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {376static_assert(cpp::is_integral_v<T>, "T must be an integral type.");377#if __has_builtin(__scoped_atomic_fetch_and)378return __scoped_atomic_fetch_and(ptr, mask, impl::order(mem_ord),379impl::scope(mem_scope));380#else381return __atomic_fetch_and(ptr, mask, impl::order(mem_ord));382#endif383}384385LIBC_INLINE T fetch_sub(386T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,387[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {388static_assert(cpp::is_integral_v<T>, "T must be an integral type.");389#if __has_builtin(__scoped_atomic_fetch_sub)390return __scoped_atomic_fetch_sub(ptr, decrement, impl::order(mem_ord),391impl::scope(mem_scope));392#else393return __atomic_fetch_sub(ptr, decrement, impl::order(mem_ord));394#endif395}396};397398// Permit CTAD when generating an atomic reference.399template <typename T> AtomicRef(T &) -> AtomicRef<T>;400401// Issue a thread fence with the given memory ordering.402LIBC_INLINE void atomic_thread_fence(403MemoryOrder mem_ord,404[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {405#if __has_builtin(__scoped_atomic_thread_fence)406__scoped_atomic_thread_fence(static_cast<int>(mem_ord),407static_cast<int>(mem_scope));408#else409__atomic_thread_fence(static_cast<int>(mem_ord));410#endif411}412413// Establishes memory synchronization ordering of non-atomic and relaxed atomic414// accesses, as instructed by order, between a thread and a signal handler415// executed on the same thread. This is equivalent to atomic_thread_fence,416// except no instructions for memory ordering are issued. Only reordering of417// the instructions by the compiler is suppressed as order instructs.418LIBC_INLINE void atomic_signal_fence([[maybe_unused]] MemoryOrder mem_ord) {419#if __has_builtin(__atomic_signal_fence)420__atomic_signal_fence(static_cast<int>(mem_ord));421#else422// if the builtin is not ready, use asm as a full compiler barrier.423asm volatile("" ::: "memory");424#endif425}426} // namespace cpp427} // namespace LIBC_NAMESPACE_DECL428429#endif // LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H430431432