folly/synchronization/RelaxedAtomic.h (257 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <atomic> #include <cstdint> namespace folly { // relaxed_atomic // // Like atomic, but without the std::memory_order parameters on any member // functions. All operations use std::memory_order_relaxed, rather than the // std::memory_order_seq_cst, which is the default memory order in std::atomic. // // Useful for values which may be loaded and stored concurrently, such as // counters, but which do not guard any associated data. template <typename T> struct relaxed_atomic; namespace detail { template <typename T> struct relaxed_atomic_base : protected std::atomic<T> { private: using atomic = std::atomic<T>; public: using value_type = T; using atomic::atomic; T operator=(T desired) noexcept { store(desired); return desired; } T operator=(T desired) volatile noexcept { store(desired); return desired; } bool is_lock_free() const noexcept { return atomic::is_lock_free(); } bool is_lock_free() const volatile noexcept { return atomic::is_lock_free(); } void store(T desired) noexcept { atomic::store(desired, std::memory_order_relaxed); } void store(T desired) volatile noexcept { atomic::store(desired, std::memory_order_relaxed); } T load() const noexcept { return atomic::load(std::memory_order_relaxed); } T load() const volatile noexcept { return atomic::load(std::memory_order_relaxed); } operator T() const noexcept { return load(); } operator T() const volatile noexcept { return load(); } T exchange(T desired) noexcept { return atomic::exchange(desired, std::memory_order_relaxed); } T exchange(T desired) volatile noexcept { return atomic::exchange(desired, std::memory_order_relaxed); } bool compare_exchange_weak(T& expected, T desired) noexcept { return atomic::compare_exchange_weak( expected, desired, std::memory_order_relaxed); } bool compare_exchange_weak(T& expected, T desired) volatile noexcept { return atomic::compare_exchange_weak( expected, desired, std::memory_order_relaxed); } bool compare_exchange_strong(T& expected, T desired) noexcept { return atomic::compare_exchange_strong( expected, desired, std::memory_order_relaxed); } bool compare_exchange_strong(T& expected, T desired) volatile noexcept { return atomic::compare_exchange_strong( expected, desired, std::memory_order_relaxed); } }; template <typename T> struct relaxed_atomic_integral_base : private relaxed_atomic_base<T> { private: using atomic = std::atomic<T>; using base = relaxed_atomic_base<T>; public: using typename base::value_type; using base::relaxed_atomic_base; using base::operator=; using base::operator T; using base::compare_exchange_strong; using base::compare_exchange_weak; using base::exchange; using base::is_lock_free; using base::load; using base::store; T fetch_add(T arg) noexcept { return atomic::fetch_add(arg, std::memory_order_relaxed); } T fetch_add(T arg) volatile noexcept { return atomic::fetch_add(arg, std::memory_order_relaxed); } T fetch_sub(T arg) noexcept { return atomic::fetch_sub(arg, std::memory_order_relaxed); } T fetch_sub(T arg) volatile noexcept { return atomic::fetch_sub(arg, std::memory_order_relaxed); } T fetch_and(T arg) noexcept { return atomic::fetch_and(arg, std::memory_order_relaxed); } T fetch_and(T arg) volatile noexcept { return atomic::fetch_and(arg, std::memory_order_relaxed); } T fetch_or(T arg) noexcept { return atomic::fetch_or(arg, std::memory_order_relaxed); } T fetch_or(T arg) volatile noexcept { return atomic::fetch_or(arg, std::memory_order_relaxed); } T fetch_xor(T arg) noexcept { return atomic::fetch_xor(arg, std::memory_order_relaxed); } T fetch_xor(T arg) volatile noexcept { return atomic::fetch_xor(arg, std::memory_order_relaxed); } T operator++() noexcept { return fetch_add(1) + 1; } T operator++() volatile noexcept { return fetch_add(1) + 1; } T operator++(int) noexcept { return fetch_add(1); } T operator++(int) volatile noexcept { return fetch_add(1); } T operator--() noexcept { return fetch_sub(1) - 1; } T operator--() volatile noexcept { return fetch_sub(1) - 1; } T operator--(int) noexcept { return fetch_sub(1); } T operator--(int) volatile noexcept { return fetch_sub(1); } T operator+=(T arg) noexcept { return fetch_add(arg) + arg; } T operator+=(T arg) volatile noexcept { return fetch_add(arg) + arg; } T operator-=(T arg) noexcept { return fetch_sub(arg) - arg; } T operator-=(T arg) volatile noexcept { return fetch_sub(arg) - arg; } T operator&=(T arg) noexcept { return fetch_and(arg) & arg; } T operator&=(T arg) volatile noexcept { return fetch_and(arg) & arg; } T operator|=(T arg) noexcept { return fetch_or(arg) | arg; } T operator|=(T arg) volatile noexcept { return fetch_or(arg) | arg; } T operator^=(T arg) noexcept { return fetch_xor(arg) ^ arg; } T operator^=(T arg) volatile noexcept { return fetch_xor(arg) ^ arg; } }; } // namespace detail template <> struct relaxed_atomic<bool> : detail::relaxed_atomic_base<bool> { private: using base = detail::relaxed_atomic_base<bool>; public: using typename base::value_type; using base::relaxed_atomic_base; using base::operator=; using base::operator bool; }; using relaxed_atomic_bool = relaxed_atomic<bool>; template <typename T> struct relaxed_atomic : detail::relaxed_atomic_base<T> { private: using base = detail::relaxed_atomic_base<T>; public: using typename base::value_type; using base::relaxed_atomic_base; using base::operator=; using base::operator T; }; template <typename T> struct relaxed_atomic<T*> : detail::relaxed_atomic_base<T*> { private: using atomic = std::atomic<T*>; using base = detail::relaxed_atomic_base<T*>; public: using typename base::value_type; using detail::relaxed_atomic_base<T*>::relaxed_atomic_base; using base::operator=; using base::operator T*; T* fetch_add(std::ptrdiff_t arg) noexcept { return atomic::fetch_add(arg, std::memory_order_relaxed); } T* fetch_add(std::ptrdiff_t arg) volatile noexcept { return atomic::fetch_add(arg, std::memory_order_relaxed); } T* fetch_sub(std::ptrdiff_t arg) noexcept { return atomic::fetch_sub(arg, std::memory_order_relaxed); } T* fetch_sub(std::ptrdiff_t arg) volatile noexcept { return atomic::fetch_sub(arg, std::memory_order_relaxed); } T* operator++() noexcept { return fetch_add(1) + 1; } T* operator++() volatile noexcept { return fetch_add(1) + 1; } T* operator++(int) noexcept { return fetch_add(1); } T* operator++(int) volatile noexcept { return fetch_add(1); } T* operator--() noexcept { return fetch_sub(1) - 1; } T* operator--() volatile noexcept { return fetch_sub(1) - 1; } T* operator--(int) noexcept { return fetch_sub(1); } T* operator--(int) volatile noexcept { return fetch_sub(1); } T* operator+=(std::ptrdiff_t arg) noexcept { return fetch_add(arg) + arg; } T* operator+=(std::ptrdiff_t arg) volatile noexcept { return fetch_add(arg) + arg; } T* operator-=(std::ptrdiff_t arg) noexcept { return fetch_sub(arg) - arg; } T* operator-=(std::ptrdiff_t arg) volatile noexcept { return fetch_sub(arg) - arg; } }; #if __cpp_deduction_guides >= 201611 template <typename T> relaxed_atomic(T) -> relaxed_atomic<T>; #endif #define FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(type) \ template <> \ struct relaxed_atomic<type> : detail::relaxed_atomic_integral_base<type> { \ private: \ using base = detail::relaxed_atomic_integral_base<type>; \ \ public: \ using base::relaxed_atomic_integral_base; \ using base::operator=; \ using base::operator type; \ }; FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(char) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(signed char) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(unsigned char) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(signed short) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(unsigned short) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(signed int) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(unsigned int) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(signed long) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(unsigned long) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(signed long long) FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION(unsigned long long) #undef FOLLY_RELAXED_ATOMIC_DEFINE_INTEGRAL_SPECIALIZATION using relaxed_atomic_char = relaxed_atomic<char>; using relaxed_atomic_schar = relaxed_atomic<signed char>; using relaxed_atomic_uchar = relaxed_atomic<unsigned char>; using relaxed_atomic_short = relaxed_atomic<short>; using relaxed_atomic_ushort = relaxed_atomic<unsigned short>; using relaxed_atomic_int = relaxed_atomic<int>; using relaxed_atomic_uint = relaxed_atomic<unsigned int>; using relaxed_atomic_long = relaxed_atomic<long>; using relaxed_atomic_ulong = relaxed_atomic<unsigned long>; using relaxed_atomic_llong = relaxed_atomic<long long>; using relaxed_atomic_ullong = relaxed_atomic<unsigned long long>; using relaxed_atomic_char16_t = relaxed_atomic<char16_t>; using relaxed_atomic_char32_t = relaxed_atomic<char32_t>; using relaxed_atomic_wchar_t = relaxed_atomic<wchar_t>; using relaxed_atomic_int8_t = relaxed_atomic<std::int8_t>; using relaxed_atomic_uint8_t = relaxed_atomic<std::uint8_t>; using relaxed_atomic_int16_t = relaxed_atomic<std::int16_t>; using relaxed_atomic_uint16_t = relaxed_atomic<std::uint16_t>; using relaxed_atomic_int32_t = relaxed_atomic<std::int32_t>; using relaxed_atomic_uint32_t = relaxed_atomic<std::uint32_t>; using relaxed_atomic_int64_t = relaxed_atomic<std::int64_t>; using relaxed_atomic_uint64_t = relaxed_atomic<std::uint64_t>; using relaxed_atomic_int_least8_t = relaxed_atomic<std::int_least8_t>; using relaxed_atomic_uint_least8_t = relaxed_atomic<std::uint_least8_t>; using relaxed_atomic_int_least16_t = relaxed_atomic<std::int_least16_t>; using relaxed_atomic_uint_least16_t = relaxed_atomic<std::uint_least16_t>; using relaxed_atomic_int_least32_t = relaxed_atomic<std::int_least32_t>; using relaxed_atomic_uint_least32_t = relaxed_atomic<std::uint_least32_t>; using relaxed_atomic_int_least64_t = relaxed_atomic<std::int_least64_t>; using relaxed_atomic_uint_least64_t = relaxed_atomic<std::uint_least64_t>; using relaxed_atomic_int_fast8_t = relaxed_atomic<std::int_fast8_t>; using relaxed_atomic_uint_fast8_t = relaxed_atomic<std::uint_fast8_t>; using relaxed_atomic_int_fast16_t = relaxed_atomic<std::int_fast16_t>; using relaxed_atomic_uint_fast16_t = relaxed_atomic<std::uint_fast16_t>; using relaxed_atomic_int_fast32_t = relaxed_atomic<std::int_fast32_t>; using relaxed_atomic_uint_fast32_t = relaxed_atomic<std::uint_fast32_t>; using relaxed_atomic_int_fast64_t = relaxed_atomic<std::int_fast64_t>; using relaxed_atomic_uint_fast64_t = relaxed_atomic<std::uint_fast64_t>; using relaxed_atomic_intptr_t = relaxed_atomic<std::intptr_t>; using relaxed_atomic_uintptr_t = relaxed_atomic<std::uintptr_t>; using relaxed_atomic_size_t = relaxed_atomic<std::size_t>; using relaxed_atomic_ptrdiff_t = relaxed_atomic<std::ptrdiff_t>; using relaxed_atomic_intmax_t = relaxed_atomic<std::intmax_t>; using relaxed_atomic_uintmax_t = relaxed_atomic<std::uintmax_t>; } // namespace folly