The Gaudi Framework  master (37c0b60a)
SynchronizedValue.h
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2023 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #pragma once
12 #include <algorithm>
13 #include <functional>
14 #include <mutex>
15 #include <shared_mutex>
16 #include <tuple>
17 #include <type_traits>
18 #include <utility>
19 
20 namespace Gaudi::cxx {
21 
22  namespace details {
23 
24  template <typename Value, typename... Args>
25  using require_constructible_t = std::enable_if_t<std::is_constructible_v<Value, Args...>>;
26 
27  } // namespace details
28 
29  // C++20: replace with http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0290r2.html
30  // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4033.html
31 
32  template <typename Value, typename Mutex = std::mutex,
33  typename ReadLock = std::conditional_t<std::is_same_v<std::shared_mutex, Mutex>, std::shared_lock<Mutex>,
35  typename WriteLock = std::lock_guard<Mutex>>
37  static_assert( !std::is_reference_v<Value>, "Value must not be a reference" );
39  mutable Mutex m_mtx;
40 
41  public:
42  template <typename... Args, typename = details::require_constructible_t<Value, Args...>>
43  SynchronizedValue( Args&&... args ) : m_obj{ std::forward<Args>( args )... } {}
44 
46  static_assert( std::is_default_constructible_v<Value> ); // bound to hold, as otherwise we wouldn't get this
47  // far... so for 'documnentation purpose' really (C++20:
48  // turn into a `requires` clause )...
49  static_assert( std::is_copy_assignable_v<Value> );
50  auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
51  m_obj = rhs.m_obj;
52  }
53 
55  static_assert( std::is_copy_assignable_v<Value> );
56  if ( this != &rhs ) {
57  auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
58  m_obj = rhs.m_obj;
59  }
60  return *this;
61  }
62 
64  static_assert( std::is_default_constructible_v<Value> ); // bound to hold, as otherwise we wouldn't get this
65  // far... so for 'documnentation purpose' really (C++20:
66  // turn into a `requires` clause )...
67  static_assert( std::is_move_assignable_v<Value> );
68  auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
69  m_obj = std::move( rhs.m_obj );
70  }
71 
73  static_assert( std::is_move_assignable_v<Value> );
74  if ( this != &rhs ) {
75  auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
76  m_obj = std::move( rhs.m_obj );
77  }
78  return *this;
79  }
80 
81  template <typename F, typename... Args,
82  typename = std::enable_if_t<std::is_invocable_v<F, Value&, Args...> &&
83  !std::is_invocable_v<F, const Value&, Args...>>>
84  decltype( auto ) with_lock( F&& f, Args&&... args ) {
85  WriteLock _{ m_mtx };
86  return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
87  }
88 
89  template <typename F, typename... Args, typename = std::enable_if_t<std::is_invocable_v<F, const Value&, Args...>>>
90  decltype( auto ) with_lock( F&& f, Args&&... args ) const {
91  ReadLock _{ m_mtx };
92  return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
93  }
94  };
95 
96  // transform an f(T,...) into an f(SynchronizedValue<T>,...)
97  template <typename Fun>
98  auto with_lock( Fun&& f ) {
99  return [f = std::forward<Fun>( f )]( auto& p, auto&&... args ) -> decltype( auto ) {
100  return p.with_lock( f, std::forward<decltype( args )>( args )... );
101  };
102  }
103  // call f(T) for each element in a container of Synced<T>
104  template <typename ContainerOfSynced, typename Fun>
105  void for_each( ContainerOfSynced& c, Fun&& f ) {
106  std::for_each( begin( c ), end( c ), with_lock( std::forward<Fun>( f ) ) );
107  }
108 
109 } // namespace Gaudi::cxx
std::for_each
T for_each(T... args)
Gaudi::cxx::SynchronizedValue::SynchronizedValue
SynchronizedValue(Args &&... args)
Definition: SynchronizedValue.h:43
Gaudi::cxx::SynchronizedValue::SynchronizedValue
SynchronizedValue(SynchronizedValue &&rhs)
Definition: SynchronizedValue.h:63
std::move
T move(T... args)
std::lock_guard
STL class.
gaudirun.c
c
Definition: gaudirun.py:525
MultiMergers.Value
Value
Definition: MultiMergers.py:15
Gaudi::cxx::SynchronizedValue::SynchronizedValue
SynchronizedValue(const SynchronizedValue &rhs)
Definition: SynchronizedValue.h:45
Gaudi::cxx::SynchronizedValue::operator=
SynchronizedValue & operator=(const SynchronizedValue &rhs)
Definition: SynchronizedValue.h:54
Gaudi::cxx::SynchronizedValue::m_mtx
Mutex m_mtx
Definition: SynchronizedValue.h:39
Gaudi::Utils::begin
AttribStringParser::Iterator begin(const AttribStringParser &parser)
Definition: AttribStringParser.h:136
Gaudi::cxx::for_each
void for_each(ContainerOfSynced &c, Fun &&f)
Definition: SynchronizedValue.h:105
details
Definition: AnyDataWrapper.h:19
Gaudi::cxx::details::require_constructible_t
std::enable_if_t< std::is_constructible_v< Value, Args... > > require_constructible_t
Definition: SynchronizedValue.h:25
std::forward
T forward(T... args)
Gaudi::cxx::SynchronizedValue::m_obj
Value m_obj
Definition: SynchronizedValue.h:37
gaudirun.args
args
Definition: gaudirun.py:336
Gaudi::cxx
Definition: SynchronizedValue.h:20
Gaudi::cxx::SynchronizedValue
Definition: SynchronizedValue.h:36
std::mutex
STL class.
IOTest.end
end
Definition: IOTest.py:125
Gaudi::cxx::SynchronizedValue::operator=
SynchronizedValue & operator=(SynchronizedValue &&rhs)
Definition: SynchronizedValue.h:72
Gaudi::cxx::with_lock
auto with_lock(Fun &&f)
Definition: SynchronizedValue.h:98
std::shared_lock
STL class.