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
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
|