Skip to content

Commit

Permalink
relaxed_atomic, atomic with assumed memory_order_relaxed
Browse files Browse the repository at this point in the history
Summary:
[Folly] `relaxed_atomic`, `atomic` with assumed `memory_order_relaxed`.

This is useful for values which are themselves atomics but which do not guard other non-atomic data, such as counters. In such cases, there is no required sequencing between the atomic value and other memory locations, just between concurrent accesses to the same atomic value, so relaxed operations are sufficient.

Reviewed By: ot

Differential Revision: D6206542

fbshipit-source-id: c820d7fcf189350a1feda4b012cb6a6342e32104
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Sep 28, 2021
1 parent 8516df3 commit c7e3550
Show file tree
Hide file tree
Showing 3 changed files with 671 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -849,6 +849,7 @@ if (BUILD_TESTS)
TEST baton_test SOURCES BatonTest.cpp
TEST call_once_test SOURCES CallOnceTest.cpp
TEST lifo_sem_test SOURCES LifoSemTests.cpp
TEST relaxed_atomic_test SOURCES RelaxedAtomicTest.cpp
TEST rw_spin_lock_test SOURCES RWSpinLockTest.cpp
TEST semaphore_test SOURCES SemaphoreTest.cpp

Expand Down
323 changes: 323 additions & 0 deletions folly/synchronization/RelaxedAtomic.h
@@ -0,0 +1,323 @@
/*
* Copyright (c) Facebook, Inc. and its 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 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 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 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 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 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;
}
};

#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

0 comments on commit c7e3550

Please sign in to comment.