Skip to content
Snippets Groups Projects
Commit 36698944 authored by Hadrien Benjamin Grasland's avatar Hadrien Benjamin Grasland
Browse files

Basic multi-slot backend

parent ab0cd4e8
No related branches found
No related tags found
No related merge requests found
#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 )
}
}
#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
#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
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment