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