libluna: Document Atomic.h
This commit is contained in:
parent
6058a69182
commit
b8e70996c3
@ -1,5 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @file Atomic.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Atomic value operations.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An enum representing the different C++11 memory orders.
|
||||||
|
*/
|
||||||
enum class MemoryOrder
|
enum class MemoryOrder
|
||||||
{
|
{
|
||||||
Relaxed = __ATOMIC_RELAXED,
|
Relaxed = __ATOMIC_RELAXED,
|
||||||
@ -10,48 +22,114 @@ enum class MemoryOrder
|
|||||||
SeqCst = __ATOMIC_SEQ_CST,
|
SeqCst = __ATOMIC_SEQ_CST,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An atomic wrapper around a simple integer value.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
*/
|
||||||
template <typename T> class Atomic
|
template <typename T> class Atomic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Atomic object.
|
||||||
|
*/
|
||||||
Atomic() : m_value()
|
Atomic() : m_value()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Atomic object.
|
||||||
|
*
|
||||||
|
* @param value The value to use.
|
||||||
|
*/
|
||||||
Atomic(T value) : m_value(value)
|
Atomic(T value) : m_value(value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value inside this object.
|
||||||
|
*
|
||||||
|
* @param other The value to store.
|
||||||
|
* @return T The updated value.
|
||||||
|
*/
|
||||||
T operator=(T other)
|
T operator=(T other)
|
||||||
{
|
{
|
||||||
store(other);
|
store(other);
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform an atomic load of the stored value.
|
||||||
|
*
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The loaded value.
|
||||||
|
*/
|
||||||
T load(MemoryOrder order = MemoryOrder::SeqCst) const
|
T load(MemoryOrder order = MemoryOrder::SeqCst) const
|
||||||
{
|
{
|
||||||
return __atomic_load_n(&m_value, (int)order);
|
return __atomic_load_n(&m_value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the stored value atomically.
|
||||||
|
*
|
||||||
|
* @return T The stored value.
|
||||||
|
*/
|
||||||
operator T() const
|
operator T() const
|
||||||
{
|
{
|
||||||
return load();
|
return load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value atomically.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
*/
|
||||||
void store(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
void store(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_store_n(&m_value, value, (int)order);
|
return __atomic_store_n(&m_value, value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value atomically, and return the old value.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The old value.
|
||||||
|
*/
|
||||||
T exchange(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
T exchange(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_exchange_n(&m_value, value, (int)order);
|
return __atomic_exchange_n(&m_value, value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_strong returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param success The memory order to use in case of success.
|
||||||
|
* @param failure The memory order to use in case of failure.
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_strong(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
bool compare_exchange_strong(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
||||||
{
|
{
|
||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)success, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)success, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_strong returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_strong(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
bool compare_exchange_strong(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
||||||
@ -60,11 +138,40 @@ template <typename T> class Atomic
|
|||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)order, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)order, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* This variant may be more performant than compare_exchange_strong, but is allowed to sometimes spuriously fail
|
||||||
|
* (fail even if the two values did match).
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_weak returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param success The memory order to use in case of success.
|
||||||
|
* @param failure The memory order to use in case of failure.
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match (or there was a spurious failure) and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_weak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
bool compare_exchange_weak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
||||||
{
|
{
|
||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)success, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)success, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* This variant may be more performant than compare_exchange_strong, but is allowed to sometimes spuriously fail
|
||||||
|
* (fail even if the two values did match).
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_weak returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match (or there was a spurious failure) and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_weak(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
bool compare_exchange_weak(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
||||||
@ -73,41 +180,87 @@ template <typename T> class Atomic
|
|||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)order, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)order, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically add a value to the stored value and return the previous value.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The original stored value.
|
||||||
|
*/
|
||||||
T fetch_add(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
T fetch_add(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_fetch_add(&m_value, other, (int)order);
|
return __atomic_fetch_add(&m_value, other, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically subtract a value from the stored value and return the previous value.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The original stored value.
|
||||||
|
*/
|
||||||
T fetch_sub(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
T fetch_sub(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_fetch_sub(&m_value, other, (int)order);
|
return __atomic_fetch_sub(&m_value, other, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increment the stored value and return it.
|
||||||
|
*
|
||||||
|
* @return T The new value.
|
||||||
|
*/
|
||||||
T operator++()
|
T operator++()
|
||||||
{
|
{
|
||||||
return fetch_add(1) + 1;
|
return fetch_add(1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch the stored value, increment it and return the original value.
|
||||||
|
*
|
||||||
|
* @return T The original value.
|
||||||
|
*/
|
||||||
T operator++(int)
|
T operator++(int)
|
||||||
{
|
{
|
||||||
return fetch_add(1);
|
return fetch_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrement the stored value and return it.
|
||||||
|
*
|
||||||
|
* @return T The new value.
|
||||||
|
*/
|
||||||
T operator--()
|
T operator--()
|
||||||
{
|
{
|
||||||
return fetch_sub(1) - 1;
|
return fetch_sub(1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch the stored value, decrement it and return the original value.
|
||||||
|
*
|
||||||
|
* @return T The original value.
|
||||||
|
*/
|
||||||
T operator--(int)
|
T operator--(int)
|
||||||
{
|
{
|
||||||
return fetch_sub(1);
|
return fetch_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a value to the stored value and return the result.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @return T The resulting value.
|
||||||
|
*/
|
||||||
T operator+=(const T& other)
|
T operator+=(const T& other)
|
||||||
{
|
{
|
||||||
return fetch_add(other) + other;
|
return fetch_add(other) + other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subtract a value from the stored value and return the result.
|
||||||
|
*
|
||||||
|
* @param other The value to subtract.
|
||||||
|
* @return T The resulting value.
|
||||||
|
*/
|
||||||
T operator-=(const T& other)
|
T operator-=(const T& other)
|
||||||
{
|
{
|
||||||
return fetch_sub(other) - other;
|
return fetch_sub(other) - other;
|
||||||
|
Loading…
Reference in New Issue
Block a user