Skip to content

Commit

Permalink
Add Locked accessor
Browse files Browse the repository at this point in the history
This accessor protects each data access using a mutex.
  • Loading branch information
bernhardmgruber committed Sep 19, 2023
1 parent 5445f92 commit 9b96595
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
55 changes: 55 additions & 0 deletions include/llama/Accessors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "macros.hpp"

#include <atomic>
#include <memory>
#include <mutex>

namespace llama::accessor
{
Expand Down Expand Up @@ -112,6 +114,59 @@ namespace llama::accessor
};
#endif

/// Locks a mutex during each access to the data structure.
template<typename Mutex = std::mutex>
struct Locked
{
// mutexes are usually not movable, so we put them on the heap, so the accessor is movable
std::unique_ptr<Mutex> m = std::make_unique<Mutex>();

template<typename Ref, typename Value>
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
struct Reference : ProxyRefOpMixin<Reference<Ref, Value>, Value>
{
Ref ref;
Mutex& m;

using value_type = Value;

// NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
LLAMA_FORCE_INLINE constexpr auto operator=(const Reference& other) -> Reference&
{
const std::lock_guard<Mutex> lock(m);
*this = static_cast<value_type>(other);
return *this;
}

// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
LLAMA_FORCE_INLINE operator value_type() const
{
const std::lock_guard<Mutex> lock(m);
return static_cast<value_type>(ref);
}

template<typename T>
LLAMA_FORCE_INLINE auto operator=(T t) -> Reference&
{
const std::lock_guard<Mutex> lock(m);
ref = t;
return *this;
}
};

template<typename PR>
LLAMA_FORCE_INLINE auto operator()(PR r) const -> Reference<PR, typename PR::value_type>
{
return {{}, r, *m};
}

template<typename T>
LLAMA_FORCE_INLINE auto operator()(T& r) const -> Reference<T&, std::remove_cv_t<T>>
{
return {{}, r, *m};
}
};

namespace internal
{
template<std::size_t I, typename Accessor>
Expand Down
2 changes: 1 addition & 1 deletion include/llama/View.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ namespace llama
LLAMA_FN_HOST_ACC_INLINE auto allocView(Mapping mapping = {}, const Allocator& alloc = {}, Accessor accessor = {})
-> View<Mapping, internal::AllocatorBlobType<Allocator, typename Mapping::RecordDim>, Accessor>
{
auto view = allocViewUninitialized(std::move(mapping), alloc, accessor);
auto view = allocViewUninitialized(std::move(mapping), alloc, std::move(accessor));
constructFields(view);
return view;
}
Expand Down
17 changes: 17 additions & 0 deletions tests/accessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "common.hpp"

#include <thread>

TEST_CASE("view.allocView.Default")
{
auto mapping = llama::mapping::AoS{llama::ArrayExtents{3, 4}, Particle{}};
Expand Down Expand Up @@ -36,6 +38,21 @@ TEST_CASE("view.allocView.Atomic")
}
#endif

TEST_CASE("view.allocView.Locked")
{
auto mapping = llama::mapping::AoS{llama::ArrayExtents{3, 4}, Vec3I{}};
auto view = llama::allocView(mapping, llama::bloballoc::Vector{}, llama::accessor::Locked{});
iotaFillView(view);
iotaCheckView(view);
// concurrent access. TSAN would detect a race here (e.g. with Default accessor, tested with clang-16).
std::thread t1{[&] { view(1, 1)(tag::X{}) = 1; }};
std::thread t2{[&] { view(1, 1)(tag::X{}) = 2; }};
std::thread t3{[&] { view(2, 2)(tag::X{}) = 3; }};
t1.join();
t2.join();
t3.join();
}

TEST_CASE("view.withAccessor.Default.Vector")
{
auto view
Expand Down

0 comments on commit 9b96595

Please sign in to comment.