From 3669894446ad9a1a44290f06cb0ec73253e116f1 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland <grasland@lal.in2p3.fr> Date: Thu, 24 Jan 2019 18:53:06 +0100 Subject: [PATCH] Basic multi-slot backend --- .../src/Conditions/ConditionBackendSvc.cpp | 46 +++++++++++++++++-- .../src/Conditions/ConditionBackendSvc.h | 32 +++++++++++-- .../src/Conditions/ConditionContext.cpp | 16 ++++++- .../src/Conditions/ConditionContext.h | 23 ++++++---- 4 files changed, 97 insertions(+), 20 deletions(-) diff --git a/GaudiExamples/src/Conditions/ConditionBackendSvc.cpp b/GaudiExamples/src/Conditions/ConditionBackendSvc.cpp index 316270189..c9e7d32f1 100644 --- a/GaudiExamples/src/Conditions/ConditionBackendSvc.cpp +++ b/GaudiExamples/src/Conditions/ConditionBackendSvc.cpp @@ -1,3 +1,4 @@ +#include <algorithm> #include <stdexcept> #include <unordered_map> @@ -7,16 +8,33 @@ namespace DemoBackend { namespace detail { + StatusCode ConditionBackendSvc::initialize() + { + auto status = Service::start(); + if ( status.isFailure() ) return status; + + m_slotUsage.reserve( m_numSlots ); + for ( size_t i = 0; i < m_numSlots; ++i ) { + m_slotUsage.push_back( false ); + } + + return status; + } + void ConditionBackendSvc::registerCondition( Gaudi::ConditionAccessorBase& accessor ) { if ( m_keysFrozen ) { throw std::runtime_error( "Attempted to register a condition input after start()" ); } + if ( accessor.type() != typeid( float ) ) { throw std::runtime_error( "This dummy condition back-end only supports float conditions" ); } - auto res = m_data.emplace( accessor.key(), std::numeric_limits<float>::infinity() ); - const float* dataPtr = &( res.first->second ); + + std::vector<float> slots( m_numSlots, std::numeric_limits<float>::infinity() ); + auto res = m_data.emplace( accessor.key(), std::move( slots ) ); + const std::vector<float>* dataPtr = &( res.first->second ); + auto id = reinterpret_cast<Gaudi::ConditionID>( dataPtr ); accessor.setID( id ); } @@ -34,11 +52,17 @@ namespace DemoBackend if ( !m_keysFrozen ) { throw std::runtime_error( "Attempted to access a condition before start()" ); } - std::unique_lock<std::mutex> dataLock( m_dataMutex ); + size_t slotIdx = selectSlot( eventTime ); for ( auto& keyVal : m_data ) { - keyVal.second = 4.2 * eventTime * keyVal.first.size(); + keyVal.second[slotIdx] = 4.2 * eventTime * keyVal.first.size(); } - return ConditionContext{ std::move( dataLock ) }; + return ConditionContext{ *this, slotIdx }; + } + + void ConditionBackendSvc::releaseSlot( size_t slotIdx ) + { + m_slotUsage[slotIdx] = false; + m_slotReady.notify_one(); } const DataObjID& ConditionBackendSvc::contextPath() const @@ -46,6 +70,18 @@ namespace DemoBackend return m_contextPath; } + size_t ConditionBackendSvc::selectSlot( const EventTimestamp& /* eventTime */ ) + { + std::unique_lock<std::mutex> slotLock{ m_slotMutex }; + auto slotIt = m_slotUsage.end(); + m_slotReady.wait( slotLock, [&]{ + slotIt = std::find( m_slotUsage.begin(), m_slotUsage.end(), false ); + return slotIt != m_slotUsage.end(); + } ); + *slotIt = true; + return std::distance( m_slotUsage.begin(), slotIt ); + } + DECLARE_COMPONENT( ConditionBackendSvc ) } } diff --git a/GaudiExamples/src/Conditions/ConditionBackendSvc.h b/GaudiExamples/src/Conditions/ConditionBackendSvc.h index 9aca0bb10..24c3e44b8 100644 --- a/GaudiExamples/src/Conditions/ConditionBackendSvc.h +++ b/GaudiExamples/src/Conditions/ConditionBackendSvc.h @@ -1,7 +1,10 @@ #pragma once +#include <condition_variable> +#include <mutex> #include <typeinfo> -#include <unordered_set> +#include <unordered_map> +#include <vector> #include "GaudiKernel/ConditionAccessorBase.h" #include "GaudiKernel/ConditionKey.h" @@ -36,6 +39,9 @@ namespace DemoBackend // Inherited Service constructor using Service::Service; + // Need to do some bookkeeping once amount of condition slots is known + StatusCode initialize() override; + // Notify the storage back-end that we will be accessing a condition // Type info is provided to be able to type-check the condition dataflow void registerCondition( Gaudi::ConditionAccessorBase& ); @@ -48,6 +54,9 @@ namespace DemoBackend // derivation or gives access to previously prepared conditions. ConditionContext setupEvent( const EventTimestamp& ); + // ConditionContext implementation detail for releasing a storage slot + void releaseSlot( size_t slotIdx ); + // TES location at which the condition context should be inserted const DataObjID& contextPath() const; @@ -56,12 +65,27 @@ namespace DemoBackend Gaudi::Property<DataObjID> m_contextPath{ this, "ContextPath", "/Event/DemoBackendCondCtx", "Path to condition context in TES" }; + // Number of condition storage slots to be used + Gaudi::Property<size_t> m_numSlots{ + this, "StorageSlots", 4, "Number of condition storage slots to use" }; + // Flag indicating when the above set can be considered complete bool m_keysFrozen = false; - // Condition storage - std::mutex m_dataMutex; - std::unordered_map<Gaudi::ConditionKey, float> m_data; + // Slots are shared mutable state, so access to them must be synchronized + std::mutex m_slotMutex; + std::condition_variable m_slotReady; + + // One entry per storage slot, indicates if the slot is used + // FIXME: Use LRU eviction policy instead of first-fit + std::vector<bool> m_slotUsage; + + // Condition data sorted by condition, one entry per storage slot + // FIXME: Have a single array of conditions to reduce allocation & memory spread + std::unordered_map<Gaudi::ConditionKey, std::vector<float>> m_data; + + // Select a storage slot for an incoming event + size_t selectSlot( const EventTimestamp& ); }; } } \ No newline at end of file diff --git a/GaudiExamples/src/Conditions/ConditionContext.cpp b/GaudiExamples/src/Conditions/ConditionContext.cpp index 0c55574ad..64af4aedf 100644 --- a/GaudiExamples/src/Conditions/ConditionContext.cpp +++ b/GaudiExamples/src/Conditions/ConditionContext.cpp @@ -1,8 +1,20 @@ +#include "ConditionBackendSvc.h" #include "ConditionContext.h" namespace DemoBackend { - ConditionContext::ConditionContext( std::unique_lock<std::mutex>&& dataLock ) - : m_dataLock{ std::move( dataLock ) } + ConditionContext::~ConditionContext() + { + if ( m_backendSvc ) m_backendSvc->releaseSlot( m_slotIdx ); + } + + ConditionContext::ConditionContext( ConditionContext&& other ) + : m_backendSvc{ std::exchange( other.m_backendSvc, nullptr ) } + , m_slotIdx{ other.m_slotIdx } + {} + + ConditionContext::ConditionContext( detail::ConditionBackendSvc& backendSvc, size_t slotIdx ) + : m_backendSvc{ &backendSvc } + , m_slotIdx{ slotIdx } {} } \ No newline at end of file diff --git a/GaudiExamples/src/Conditions/ConditionContext.h b/GaudiExamples/src/Conditions/ConditionContext.h index 4a3249df1..a174276fe 100644 --- a/GaudiExamples/src/Conditions/ConditionContext.h +++ b/GaudiExamples/src/Conditions/ConditionContext.h @@ -3,6 +3,7 @@ #include <mutex> #include <stdexcept> #include <typeinfo> +#include <vector> #include "GaudiKernel/DataObject.h" #include "GaudiKernel/ConditionID.h" @@ -24,12 +25,13 @@ namespace DemoBackend { public: // Upon going out of scope the ConditionContext will mark the conditions as unused - ~ConditionContext() = default; + ~ConditionContext(); - // The ConditionContext is movable but not copyable - ConditionContext( ConditionContext&& ) = default; + // The ConditionContext should be neither moved nor copied, but we can't + // prevent you from moving it until C++17 is the norm + ConditionContext( ConditionContext&& ); ConditionContext( const ConditionContext& ) = delete; - ConditionContext& operator=( ConditionContext&& ) = default; + ConditionContext& operator=( ConditionContext&& ) = delete; ConditionContext& operator=( const ConditionContext& ) = delete; private: @@ -44,8 +46,8 @@ namespace DemoBackend if ( typeid( ConditionType ) != typeid( float ) ) { throw std::runtime_error( "This silly backend only supports float conditions" ); } - auto dataPtr = reinterpret_cast<const float*>( id ); - return *dataPtr; + auto dataPtr = reinterpret_cast<const std::vector<float>*>( id ); + return (*dataPtr)[m_slotIdx]; } private: @@ -53,10 +55,13 @@ namespace DemoBackend friend class detail::ConditionBackendSvc; // Construct a ConditionContext from backend-internal data - ConditionContext( std::unique_lock<std::mutex>&& dataLock ); + ConditionContext( detail::ConditionBackendSvc& backend, size_t slotIdx ); private: - // This silly implementation is actually locking - std::unique_lock<std::mutex> m_dataLock; + // Must tell the ConditionBackendSvc to release our slot when destroyed + detail::ConditionBackendSvc* m_backendSvc; + + // This is the condition slot index that we shall be targeting + size_t m_slotIdx; }; } \ No newline at end of file -- GitLab