Skip to content

Commit

Permalink
Merge pull request #215 from pnnl/atomic_ops
Browse files Browse the repository at this point in the history
Atomic ops
  • Loading branch information
VitoCastellana authored Mar 6, 2022
2 parents 74ff4b6 + b96ab4c commit dc66fbe
Show file tree
Hide file tree
Showing 2 changed files with 481 additions and 0 deletions.
269 changes: 269 additions & 0 deletions include/shad/data_structures/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,114 @@ class Atomic : public AbstractDataStructure<Atomic<T>> {
return;
}

/// @brief Atomic Store. Attempts at atomically storing the results of binop.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const ArgT& rhs).
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
/// @return true if the store was successful, false otherwise.
template <typename ArgT, typename BinaryOp>
bool Store(ArgT desired_arg, BinaryOp binop) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
return atomic_compare_exchange_strong(&localInstance_,
&old_value, desired);
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](const StoreArgs &args, bool *res) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto binop = std::get<2>(args);
T desired = binop(old_value, std::get<1>(args));
*res = atomic_compare_exchange_strong(&(ptr->localInstance_),
&old_value, desired);
};
StoreArgs args(oid_, desired_arg, binop);
bool res;
rt::executeAtWithRet(ownerLoc_, StoreFun, args, &res);
return res;
}

/// @brief Atomic Store. Attempts at atomically storing the results of binop
/// unitil succesful.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const ArgT& rhs).
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
template <typename ArgT, typename BinaryOp>
void ForceStore(ArgT desired_arg, BinaryOp binop) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&localInstance_,
&old_value, desired)) {
old_value = localInstance_.load();
desired = binop(old_value, desired_arg);
}
return;
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](const StoreArgs &args) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto desired_arg = std::get<1>(args);
auto binop = std::get<2>(args);
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&ptr->localInstance_,
&old_value, desired)) {
old_value = ptr->localInstance_.load();
desired = binop(old_value, desired_arg);
}
};
StoreArgs args(oid_, desired_arg, binop);
rt::executeAt(ownerLoc_, StoreFun, args);
}


/// @brief Async Atomic Fetch-Store. Attempts at atomically storing
/// the results of binop unitil succesful.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const ArgT& rhs).
/// @param[in,out] h The handle to be used to wait for completion.
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
/// @return The value fetched when Store was successful.
template <typename ArgT, typename BinaryOp>
T ForceFetchStore(ArgT desired_arg, BinaryOp binop) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&localInstance_,
&old_value, desired)) {
old_value = localInstance_.load();
desired = binop(old_value, desired_arg);
}
return old_value;
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](const StoreArgs &args, T*res) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto desired_arg = std::get<1>(args);
auto binop = std::get<2>(args);
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&ptr->localInstance_,
&old_value, desired)) {
old_value = ptr->localInstance_.load();
desired = binop(old_value, desired_arg);
}
*res = old_value;
};
StoreArgs args(oid_, desired_arg, binop);
T res;
rt::executeAtWithRet(ownerLoc_, StoreFun, args, &res);
return res;
}

/// @brief Async Atomic Store.
///
/// @param[in,out] h The handle to be used to wait for completion.
Expand All @@ -131,6 +239,167 @@ class Atomic : public AbstractDataStructure<Atomic<T>> {
return;
}

/// @brief Async Atomic Store. Attempts at atomically storing the results of binop.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const T& rhs).
/// @param[in,out] h The handle to be used to wait for completion.
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
/// @param[out] res Pointer to the region where the result is
/// written; res must point to a valid memory allocation.
/// *res is true if the store operation was successful, false otherwise.
template <typename ArgT, typename BinaryOp>
void AsyncStore(rt::Handle& h, ArgT desired_arg, BinaryOp binop, bool* res) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
*res = atomic_compare_exchange_strong(&localInstance_,
&old_value, desired);
return;
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](rt::Handle&, const StoreArgs &args, bool *res) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto binop = std::get<2>(args);
T desired = binop(old_value, std::get<1>(args));
*res = atomic_compare_exchange_strong(&(ptr->localInstance_),
&old_value, desired);
};
StoreArgs args(oid_, desired_arg, binop);
rt::asyncExecuteAtWithRet(h, ownerLoc_, StoreFun, args, res);
}

/// @brief Async Atomic Store. Attempts at atomically storing
/// the results of binop unitil succesful.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const ArgT& rhs).
/// @param[in,out] h The handle to be used to wait for completion.
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
template <typename ArgT, typename BinaryOp>
void AsyncForceStore(rt::Handle &h, ArgT desired_arg, BinaryOp binop) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&localInstance_,
&old_value, desired)) {
old_value = localInstance_.load();
desired = binop(old_value, desired_arg);
}
return;
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](rt::Handle&, const StoreArgs &args) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto desired_arg = std::get<1>(args);
auto binop = std::get<2>(args);
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&ptr->localInstance_,
&old_value, desired)) {
old_value = ptr->localInstance_.load();
desired = binop(old_value, desired_arg);
}
};
StoreArgs args(oid_, desired_arg, binop);
rt::asyncExecuteAt(h, ownerLoc_, StoreFun, args);
}

/// @brief Async Atomic Fetch-Store. Attempts at atomically storing
/// the results of binop unitil succesful.
///
/// @tparam ArgT Type of rhs for the BinaryOp operator.
/// @tparam BinaryOp User-defined binary operator T (const T& lhs, const ArgT& rhs).
/// @param[in,out] h The handle to be used to wait for completion.
/// @param[in] desired_arg Non atomic rhs value for binop.
/// @param[in] binop Binary operator. lhs is atomic's value, rhs is desired_arg.
/// @param[out] res Pointer to the region where the result is
/// written; res must point to a valid memory allocation.
/// Result is the value fetched when Store was successful
template <typename ArgT, typename BinaryOp>
void AsyncForceFetchStore(rt::Handle &h, ArgT desired_arg,
BinaryOp binop, T* res) {
if (ownerLoc_ == rt::thisLocality()) {
auto old_value = localInstance_.load();
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&localInstance_,
&old_value, desired)) {
old_value = localInstance_.load();
desired = binop(old_value, desired_arg);
}
*res = old_value;
return;
}
using StoreArgs = std::tuple<ObjectID, ArgT, BinaryOp>;
auto StoreFun = [](rt::Handle&, const StoreArgs &args, T* res) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
auto old_value = ptr->localInstance_.load();
auto desired_arg = std::get<1>(args);
auto binop = std::get<2>(args);
T desired = binop(old_value, desired_arg);
while(!atomic_compare_exchange_weak(&ptr->localInstance_,
&old_value, desired)) {
old_value = ptr->localInstance_.load();
desired = binop(old_value, desired_arg);
}
*res = old_value;
};
StoreArgs args(oid_, desired_arg, binop);
rt::asyncExecuteAtWithRet(h, ownerLoc_, StoreFun, args, res);
}

/// @brief Compare and exchange operation.
///
/// @param[in] expected Value expected to be found in the atomic object.
/// @param[in] desired Value to store in the atomic object if it is as expected.
/// @return true if the atomic object was equal to expected, false otherwise.
bool CompareExchange(T expected, T desired) {
if (ownerLoc_ == rt::thisLocality()) {
return atomic_compare_exchange_strong(&localInstance_,
&expected, desired );
}
bool ret;
using CasArgs = std::tuple<ObjectID, T, T>;
auto CasFun = [](const CasArgs &args, bool *result) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
T expected = std::get<1>(args);
*result = atomic_compare_exchange_strong(&(ptr->localInstance_),
&expected,
std::get<2>(args));
};
CasArgs args(oid_, expected, desired);
rt::executeAtWithRet(ownerLoc_, CasFun, args, &ret);
return ret;
}

/// @brief Compare and exchange operation.
///
/// @param[in,out] h The handle to be used to wait for completion.
/// @param[in] expected Value expected to be found in the atomic object.
/// @param[in] desired Value to store in the atomic object if it is as expected.
/// @param[out] res Pointer to the region where the result is
/// written; res must point to a valid memory allocation.
/// *res is true if the atomic object was equal to expected, false otherwise.
void AsyncCompareExchange(rt::Handle &h, T expected, T desired, bool* res) {
if (ownerLoc_ == rt::thisLocality()) {
*res = atomic_compare_exchange_strong(&localInstance_,
&expected, desired);
}
using CasArgs = std::tuple<ObjectID, T, T>;
auto CasFun = [](rt::Handle&, const CasArgs &args, bool *result) {
auto ptr = Atomic<T>::GetPtr(std::get<0>(args));
T expected = std::get<1>(args);
*result = atomic_compare_exchange_strong(&(ptr->localInstance_),
&expected,
std::get<2>(args));
};
CasArgs args(oid_, expected, desired);
rt::asyncExecuteAtWithRet(h, ownerLoc_, CasFun, args, res);
}

/// @brief Fetch Add operation.
///
/// @param[in] add Data to be fetch-added.
Expand Down
Loading

0 comments on commit dc66fbe

Please sign in to comment.