Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:54:24

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 <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   } // namespace details
0028 
0029   // C++20: replace with http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0290r2.html
0030   //         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4033.html
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> ); // bound to hold, as otherwise we wouldn't get this
0047                                                                // far... so for 'documnentation purpose' really (C++20:
0048                                                                // turn into a `requires` clause )...
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> ); // bound to hold, as otherwise we wouldn't get this
0065                                                                // far... so for 'documnentation purpose' really (C++20:
0066                                                                // turn into a `requires` clause )...
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   // transform an f(T,...) into an f(SynchronizedValue<T>,...)
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   // call f(T) for each element in a container of Synced<T>
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 } // namespace Gaudi::cxx