The Gaudi Framework  master (82fdf313)
Loading...
Searching...
No Matches
SynchronizedValue.h
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 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
20namespace Gaudi::cxx {
21
22 // C++23: replace with http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0290r2.html
23 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4033.html
24
25 template <typename Value, typename Mutex = std::mutex,
26 typename ReadLock = std::conditional_t<std::is_same_v<std::shared_mutex, Mutex>, std::shared_lock<Mutex>,
27 std::lock_guard<Mutex>>,
28 typename WriteLock = std::lock_guard<Mutex>>
30 static_assert( !std::is_reference_v<Value>, "Value must not be a reference" );
31 Value m_obj;
32 mutable Mutex m_mtx;
33
34 public:
35 template <typename... Args>
36 requires( std::is_constructible_v<Value, Args...> )
37 SynchronizedValue( Args&&... args ) : m_obj{ std::forward<Args>( args )... } {}
38
40 static_assert( std::is_default_constructible_v<Value> ); // bound to hold, as otherwise we wouldn't get this
41 // far... so for 'documnentation purpose' really (C++20:
42 // turn into a `requires` clause )...
43 static_assert( std::is_copy_assignable_v<Value> );
44 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
45 m_obj = rhs.m_obj;
46 }
47
49 static_assert( std::is_copy_assignable_v<Value> );
50 if ( this != &rhs ) {
51 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
52 m_obj = rhs.m_obj;
53 }
54 return *this;
55 }
56
58 static_assert( std::is_default_constructible_v<Value> ); // bound to hold, as otherwise we wouldn't get this
59 // far... so for 'documnentation purpose' really (C++20:
60 // turn into a `requires` clause )...
61 static_assert( std::is_move_assignable_v<Value> );
62 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
63 m_obj = std::move( rhs.m_obj );
64 }
65
67 static_assert( std::is_move_assignable_v<Value> );
68 if ( this != &rhs ) {
69 auto lock = std::scoped_lock{ rhs.m_mtx, m_mtx };
70 m_obj = std::move( rhs.m_obj );
71 }
72 return *this;
73 }
74
75 template <typename... Args, std::invocable<Value&, Args...> F>
76 requires( !std::is_invocable_v<F, const Value&, Args...> )
77 decltype( auto ) with_lock( F&& f, Args&&... args ) {
78 WriteLock _{ m_mtx };
79 return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
80 }
81
82 template <typename... Args, std::invocable<const Value&, Args...> F>
83 decltype( auto ) with_lock( F&& f, Args&&... args ) const {
84 ReadLock _{ m_mtx };
85 return std::invoke( std::forward<F>( f ), m_obj, std::forward<Args>( args )... );
86 }
87 };
88
89 // transform an f(T,...) into an f(SynchronizedValue<T>,...)
90 template <typename Fun>
91 auto with_lock( Fun&& f ) {
92 return [f = std::forward<Fun>( f )]( auto& p, auto&&... args ) -> decltype( auto ) {
93 return p.with_lock( f, std::forward<decltype( args )>( args )... );
94 };
95 }
96 // call f(T) for each element in a container of Synced<T>
97 template <typename ContainerOfSynced, typename Fun>
98 void for_each( ContainerOfSynced& c, Fun&& f ) {
99 std::for_each( begin( c ), end( c ), with_lock( std::forward<Fun>( f ) ) );
100 }
101
102} // namespace Gaudi::cxx
decltype(auto) with_lock(F &&f, Args &&... args) const
SynchronizedValue(const SynchronizedValue &rhs)
SynchronizedValue(SynchronizedValue &&rhs)
SynchronizedValue & operator=(const SynchronizedValue &rhs)
decltype(auto) with_lock(F &&f, Args &&... args)
SynchronizedValue & operator=(SynchronizedValue &&rhs)
void for_each(ContainerOfSynced &c, Fun &&f)
auto with_lock(Fun &&f)