Skip to content

Instantly share code, notes, and snippets.

@phniix
Last active December 30, 2017 05:04
Show Gist options
  • Save phniix/ef90a6fa6e77da0723ff856e7f664638 to your computer and use it in GitHub Desktop.
Save phniix/ef90a6fa6e77da0723ff856e7f664638 to your computer and use it in GitHub Desktop.
A thread safe Atomic Enumeration Type building block. Uses SFINAE to enable the only valid constructor with reasoning to prevent users from shooting themselves in the foot. Many IDEs will signal that something is not quite right if incorrect usage is attempted.
#pragma once
///
/// \file atomic_enum.h
///
/// \brief An Atomic Enum object type
///
///
#include <atomic>
#include <type_traits>
///
/// \brief The namespace used by pheonix.
///
namespace phx
{
///
/// \brief Provides secure atomic operations for enumeration types.
///
/// Usage of this class must ensure a valid enumeration type, otherwise a failure will occur.
/// You should hopefully notice incorrect usage of this class in your IDE, before you compile.
///
/// This class aims to protect from incorrect usage by using making the constructor available
/// only if type EnumType is valid.
///
/// Thread safe: Yes.
///
/// \tparam EnumType An enumeration type, one that is supported by versions of C++11 onwards.
/// ie: scoped or unscoped enumeration types.
///
template <typename EnumType>
class AtomicEnum
{
public:
/// Default Constructor, deleted
AtomicEnum() = delete;
/// Copy Constructor, deleted
AtomicEnum(const AtomicEnum&) = delete;
/// Move Constructor, deleted
AtomicEnum(AtomicEnum&&) = delete;
/// Copy Assignment, deleted
AtomicEnum& operator=(const AtomicEnum&) = delete;
/// Move Assignment, deleted
AtomicEnum& operator=(AtomicEnum&&) = delete;
/// Destructor, default compiler implementation
~AtomicEnum() = default;
///
/// \brief Constructor that takes the initial value for the EnumType.
///
/// Incompatible types do not enable this constructor, rendering this class unusable.
///
/// Thread safe: Yes.
///
/// This constructor becomes available only if EnumType passes SFINAE tests that qualify it to be used by
/// std::atomic<T>.
///
/// \param [in] val EnumType
/// The value to initialize the atomic enum with.
///
/// \tparam T EnumType alias for use in the template
///
/// \tparam = Stores the result of the evaluation criteria for EnumType's candidacy to being used as an atomic type.
///
template <typename T = EnumType
, typename = typename std::enable_if<
std::is_enum<T>::value &&
std::is_trivially_constructible<T>::value &&
std::is_trivially_copyable<T>::value
>::type
>
AtomicEnum(const EnumType& val)
{
_enum.store(val, std::memory_order_release);
}
///
/// \brief Reads the current value of EnumType.
///
/// Thread safe: Yes.
///
/// \return The current value of EnumType.
///
EnumType current_value() const
{
return _enum.load(std::memory_order_acquire);
}
///
/// \brief Switch from the current value of EnumType to a new value.
///
/// Implementation performs a weak CAS, allowing spurious failures
/// to occur. This implementation may not suite all architectures.
/// Consult with you compiler/vendor if this operation may be too
/// costly for your target architecture.
///
/// Thread safe: Yes.
///
/// \param [in] next_value
/// The value of EnumType that this object should switch to.
///
void switch_to(const EnumType& new_value)
{
EnumType current_value = _enum.load(std::memory_order_relaxed);
while(!_enum.compare_exchange_weak(current_value,
new_value,
std::memory_order_release)
&& current_value != new_value);
}
private:
/// Atomic EnumType
std::atomic<EnumType> _enum;
};
} // end phx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment