Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:29

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2023 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
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   } // namespace details
0027 
0028   // C++20: replace with http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0290r2.html
0029   //         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4033.html
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> ); // bound to hold, as otherwise we wouldn't get this
0046                                                                // far... so for 'documnentation purpose' really (C++20:
0047                                                                // turn into a `requires` clause )...
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> ); // bound to hold, as otherwise we wouldn't get this
0064                                                                // far... so for 'documnentation purpose' really (C++20:
0065                                                                // turn into a `requires` clause )...
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   // transform an f(T,...) into an f(SynchronizedValue<T>,...)
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   // call f(T) for each element in a container of Synced<T>
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 } // namespace Gaudi::cxx