The Gaudi Framework  master (9ef1c226)
Loading...
Searching...
No Matches
SynchronizedValue.h
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2026 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 <concepts>
14#include <functional>
15#include <mutex>
16#include <shared_mutex>
17#include <tuple>
18#include <type_traits>
19#include <utility>
20
21namespace Gaudi::cxx {
22
23 // C++23: replace with http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0290r2.html
24 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4033.html
25
26 template <typename Value, typename Mutex = std::mutex,
27 typename ReadLock = std::conditional_t<std::is_same_v<std::shared_mutex, Mutex>, std::shared_lock<Mutex>,
28 std::lock_guard<Mutex>>,
29 typename WriteLock = std::lock_guard<Mutex>>
30 requires( !std::is_reference_v<Value> ) // Value must not be a reference
32 Value m_obj;
33 mutable Mutex m_mtx;
34
35 public:
36 template <typename... Args>
37 requires std::constructible_from<Value, Args...> &&
38 ( sizeof...( Args ) != 1 || !( std::same_as<SynchronizedValue, std::remove_cvref_t<Args>> || ... ) )
39 explicit( sizeof...( Args ) == 1 ) SynchronizedValue( Args&&... args ) : m_obj( std::forward<Args>( args )... ) {}
40
42 requires std::constructible_from<Value, Value const&>
43 : m_obj( [&] {
44 auto _ = ReadLock{ rhs.m_mtx };
45 return Value( rhs.m_obj );
46 }() ) {}
47
49 requires std::assignable_from<Value&, Value const&>
50 {
51 if ( this != &rhs ) {
52 auto _ = std::scoped_lock{ rhs.m_mtx, m_mtx };
53 m_obj = rhs.m_obj;
54 }
55 return *this;
56 }
57
59 requires std::constructible_from<Value, Value&&>
60 : m_obj( [&] {
61 auto _ = WriteLock{ rhs.m_mtx };
62 return Value( std::move( rhs.m_obj ) );
63 }() ) {}
64
66 requires std::assignable_from<Value&, Value&&>
67 {
68 if ( this != &rhs ) {
69 auto _ = 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 auto _ = 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 auto _ = 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 & operator=(const SynchronizedValue &rhs)
SynchronizedValue(SynchronizedValue const &rhs)
SynchronizedValue(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)
STL namespace.