From 063916d70f794f4b30365d927408388295eb7053 Mon Sep 17 00:00:00 2001 From: Bernhard Manfred Gruber Date: Fri, 15 Sep 2023 21:17:18 +0200 Subject: [PATCH] Add Locked accessor This accessor protects each data access using a mutex. --- include/llama/Accessors.hpp | 55 +++++++++++++++++++++++++++++++++++++ include/llama/View.hpp | 2 +- tests/accessor.cpp | 17 ++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/include/llama/Accessors.hpp b/include/llama/Accessors.hpp index b35df09212..671f9f27b0 100644 --- a/include/llama/Accessors.hpp +++ b/include/llama/Accessors.hpp @@ -8,6 +8,8 @@ #include "macros.hpp" #include +#include +#include namespace llama::accessor { @@ -112,6 +114,59 @@ namespace llama::accessor }; #endif + /// Locks a mutex during each access to the data structure. + template + struct Locked + { + // mutexes are usually not movable, so we put them on the heap, so the accessor is movable + std::unique_ptr m = std::make_unique(); + + template + // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) + struct Reference : ProxyRefOpMixin, 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 lock(m); + *this = static_cast(other); + return *this; + } + + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + LLAMA_FORCE_INLINE operator value_type() const + { + const std::lock_guard lock(m); + return static_cast(ref); + } + + template + LLAMA_FORCE_INLINE auto operator=(T t) -> Reference& + { + const std::lock_guard lock(m); + ref = t; + return *this; + } + }; + + template + LLAMA_FORCE_INLINE auto operator()(PR r) const -> Reference + { + return {{}, r, *m}; + } + + template + LLAMA_FORCE_INLINE auto operator()(T& r) const -> Reference> + { + return {{}, r, *m}; + } + }; + namespace internal { template diff --git a/include/llama/View.hpp b/include/llama/View.hpp index fe2eb83b34..8f0661acf8 100644 --- a/include/llama/View.hpp +++ b/include/llama/View.hpp @@ -151,7 +151,7 @@ namespace llama LLAMA_FN_HOST_ACC_INLINE auto allocView(Mapping mapping = {}, const Allocator& alloc = {}, Accessor accessor = {}) -> View, Accessor> { - auto view = allocViewUninitialized(std::move(mapping), alloc, accessor); + auto view = allocViewUninitialized(std::move(mapping), alloc, std::move(accessor)); constructFields(view); return view; } diff --git a/tests/accessor.cpp b/tests/accessor.cpp index ce0efa1efe..38928f9b60 100644 --- a/tests/accessor.cpp +++ b/tests/accessor.cpp @@ -3,6 +3,8 @@ #include "common.hpp" +#include + TEST_CASE("view.allocView.Default") { auto mapping = llama::mapping::AoS{llama::ArrayExtents{3, 4}, Particle{}}; @@ -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