You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
7.1 KiB

/*
* Copyright 2002-2019 Intel Corporation.
*
* This software and the related documents are Intel copyrighted materials, and your
* use of them is governed by the express license under which they were provided to
* you ("License"). Unless the License provides otherwise, you may not use, modify,
* copy, publish, distribute, disclose or transmit this software or the related
* documents without Intel's prior written permission.
*
* This software and the related documents are provided as is, with no express or
* implied warranties, other than those that are expressly stated in the License.
*/
// <COMPONENT>: atomic
// <FILE-TYPE>: component public header
#ifndef ATOMIC_OPS_HPP
#define ATOMIC_OPS_HPP
#include "atomic/exponential-backoff.hpp"
#include "atomic/ops-enum.hpp"
#include "atomic/private/ops-common-impl.hpp"
#include "atomic/private/backoff-impl.hpp"
#if defined(HOST_IA32)
# include "atomic/private/ia32/ops-impl.hpp"
#elif defined(HOST_IA32E)
# include "atomic/private/intel64/ops-impl.hpp"
#endif
namespace ATOMIC {
/*! @brief Low-level atomic memory operations.
*
* A collection of low-level atomic memory operations that can be performed on fundemental data types
* (integers, pointers, etc.) All of the operations are static template methods, so they automatically
* adapt to whatever data type you use.
*
* @par Example:
* \code
* #include "atomic/ops.hpp"
*
* int Count;
*
* void Foo()
* {
* ATOMIC::OPS::Increment(&Count, 1);
* }
* \endcode
*/
class /*<UTILITY>*/ OPS
{
public:
/*!
* Perform an atomic compare-and-swap.
*
* @param[in] location Pointer to the location to modify.
* @param[in] oldVal The \a location is only changed if its value is
* equal to \a oldVal.
* @param[in] newVal The new value to write to \a location.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*
* @return The value of \a location prior to the operation. The \a newVal was stored
* only if the return value is equal to \a oldVal.
*/
template<typename T> static T CompareAndSwap(volatile T *location, T oldVal, T newVal,
BARRIER_CS barrier = BARRIER_CS_NONE)
{
OPS_IMPL::CompareAndSwap<sizeof(T)>(static_cast<volatile void*>(location),
static_cast<const void*>(&oldVal), static_cast<void*>(&newVal), barrier);
return newVal;
}
/*!
* This is exactly the same as CompareAndSwap() except the return value is a
* boolean.
*
* @param[in] location Pointer to the location to modify.
* @param[in] oldVal The \a location is only changed if its value is
* equal to \a oldVal.
* @param[in] newVal The new value to write to \a location.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*
* @return TRUE if \a newVal was stored to \a location.
*/
template<typename T> static bool CompareAndDidSwap(volatile T *location, T oldVal, T newVal,
BARRIER_CS barrier = BARRIER_CS_NONE)
{
OPS_IMPL::CompareAndSwap<sizeof(T)>(static_cast<volatile void*>(location),
static_cast<const void*>(&oldVal), static_cast<void*>(&newVal), barrier);
return (newVal == oldVal);
}
/*!
* Perform an atomic swap.
*
* @param[in] location Pointer to the location to modify.
* @param[in] newVal The new value to write to \a location.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*
* @return The value swapped out of \a location
*/
template<typename T> static T Swap(volatile T *location, T newVal,
BARRIER_SWAP barrier = BARRIER_SWAP_NONE)
{
T oldVal;
OPS_IMPL::Swap<sizeof(T)>(static_cast<volatile void*>(location),
static_cast<void*>(&oldVal),
static_cast<const void*>(&newVal),
barrier);
return oldVal;
}
/*!
* Atomically set the value of a location. Any outside observer using an OPS method
* is guaranteed to see either the old value or the new value (but not an intermediate value).
*
* @param[in] location Pointer to the location.
* @param[in] val The value to write to \a location.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*/
template<typename T> static void Store(volatile T *location, T val, BARRIER_ST barrier = BARRIER_ST_NONE)
{
OPS_IMPL::Store<sizeof(T)>(static_cast<volatile void*>(location), static_cast<const void*>(&val), barrier);
}
/*!
* Atomically get the value of a location. If another thread changes the location using an
* OPS method, Load() is guaranteed to either return the old value or the new value (but not
* an intermediate value).
*
* @param[in] location Pointer to the location.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*
* @return The value at \a location.
*/
template<typename T> static T Load(volatile const T *location, BARRIER_LD barrier = BARRIER_LD_NONE)
{
T val;
OPS_IMPL::Load<sizeof(T)>(static_cast<volatile const void*>(location), static_cast<void*>(&val), barrier);
return val;
}
/*!
* Atomically increment a value.
*
* @param[in] location Pointer to the location to modify.
* @param[in] inc The increment amount.
* @param[in] barrier Tells the memory ordering semantics of this operation.
*
* @return The value of \a location prior to being incremented.
*/
template<typename T> static T Increment(volatile T *location, T inc,
BARRIER_CS barrier = BARRIER_CS_NONE)
{
T oldVal;
OPS_IMPL::Increment<sizeof(T)>(static_cast<volatile void*>(location), static_cast<const void*>(&inc),
static_cast<void*>(&oldVal), barrier);
return oldVal;
}
/*!
* Atomically set a location if it is less than a value. This allows you to
* compute the maximum of a series of values.
*
* @param[in] location Pointer to the location to modify.
* @param[in] val If \a location is less than \a val it is updated.
*/
template<typename T> static void MaxValue(volatile T *location, T val)
{
EXPONENTIAL_BACKOFF<> backoff;
T oldVal = Load(location);
while (oldVal < val && !CompareAndDidSwap(location, oldVal, val))
{
backoff.Delay();
oldVal = Load(location);
}
}
/*!
* Delay the calling thread in a spin loop, using a processor specific
* "spin loop hint" if available.
*
* @param[in] delay The number of iterations for the spin loop.
*/
static void Delay(unsigned delay)
{
ATOMIC_SpinDelay(static_cast<UINT32>(delay));
}
};
} // namespace
#endif // file guard