File indexing completed on 2025-01-18 09:57:29
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #pragma once
0012 #include <functional>
0013 #include <mutex>
0014 #include <shared_mutex>
0015 #include <tuple>
0016 #include <type_traits>
0017 #include <utility>
0018
0019 namespace Gaudi::cxx {
0020
0021 namespace details {
0022
0023 template <typename Value, typename... Args>
0024 using require_constructible_t = std::enable_if_t<std::is_constructible_v<Value, Args...>>;
0025
0026 }
0027
0028
0029
0030
0031 template <typename Value, typename Mutex = std::mutex,
0032 typename ReadLock = std::conditional_t<std::is_same_v<std::shared_mutex, Mutex>, std::shared_lock<Mutex>,
0033 std::lock_guard<Mutex>>,
0034 typename WriteLock = std::lock_guard<Mutex>>
0035 class SynchronizedValue {
0036 static_assert( !std::is_reference_v<Value>, "Value must not be a reference" );
0037 Value m_obj;
0038 mutable Mutex m_mtx;
0039
0040 public:
0041 template <typename... Args, typename = details::require_constructible_t<Value, Args...>>
0042 SynchronizedValue( Args&&... args ) : m_obj{ std::forward<Args>( args )... } {}
0043
0044 SynchronizedValue( const SynchronizedValue& rhs ) {
0045 static_assert( std::is_default_constructible_v<Value> );
0046
0047
0048 static_assert( std::is_copy_assignable_v<Value> );
0049 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
0050 m_obj = rhs.m_obj;
0051 }
0052
0053 SynchronizedValue& operator=( const SynchronizedValue& rhs ) {
0054 static_assert( std::is_copy_assignable_v<Value> );
0055 if ( this != &rhs ) {
0056 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
0057 m_obj = rhs.m_obj;
0058 }
0059 return *this;
0060 }
0061
0062 SynchronizedValue( SynchronizedValue&& rhs ) {
0063 static_assert( std::is_default_constructible_v<Value> );
0064
0065
0066 static_assert( std::is_move_assignable_v<Value> );
0067 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
0068 m_obj = std::move( rhs.m_obj );
0069 }
0070
0071 SynchronizedValue& operator=( SynchronizedValue&& rhs ) {
0072 static_assert( std::is_move_assignable_v<Value> );
0073 if ( this != &rhs ) {
0074 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
0075 m_obj = std::move( rhs.m_obj );
0076 }
0077 return *this;
0078 }
0079
0080 template <typename F, typename... Args,
0081 typename = std::enable_if_t<std::is_invocable_v<F, Value&, Args...> &&
0082 !std::is_invocable_v<F, const Value&, Args...>>>
0083 decltype( auto ) with_lock( F&& f, Args&&... args ) {
0084 WriteLock _{ m_mtx };
0085 return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
0086 }
0087
0088 template <typename F, typename... Args, typename = std::enable_if_t<std::is_invocable_v<F, const Value&, Args...>>>
0089 decltype( auto ) with_lock( F&& f, Args&&... args ) const {
0090 ReadLock _{ m_mtx };
0091 return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
0092 }
0093 };
0094
0095
0096 template <typename Fun>
0097 auto with_lock( Fun&& f ) {
0098 return [f = std::forward<Fun>( f )]( auto& p, auto&&... args ) -> decltype( auto ) {
0099 return p.with_lock( f, std::forward<decltype( args )>( args )... );
0100 };
0101 }
0102
0103 template <typename ContainerOfSynced, typename Fun>
0104 void for_each( ContainerOfSynced& c, Fun&& f ) {
0105 std::for_each( begin( c ), end( c ), with_lock( std::forward<Fun>( f ) ) );
0106 }
0107
0108 }