The Gaudi Framework  master (37c0b60a)
PluginServiceV1.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 2013-2024 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 
13 
14 #define GAUDI_PLUGIN_SERVICE_V1
16 
17 #include <dirent.h>
18 #include <dlfcn.h>
19 
20 #include <cstdlib>
21 #include <fstream>
22 #include <iostream>
23 #include <memory>
24 #include <regex>
25 
26 #include <cxxabi.h>
27 #include <sys/stat.h>
28 
29 namespace {
30  std::mutex registrySingletonMutex;
31 }
32 
33 #include <algorithm>
34 
35 namespace {
36  // string trimming functions taken from
37  // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
38 
39  constexpr struct is_space_t {
40  bool operator()( int i ) const { return std::isspace( i ); }
41  } is_space{};
42 
43  // trim from start
44  static inline std::string& ltrim( std::string& s ) {
45  s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) );
46  return s;
47  }
48 
49  // trim from end
50  static inline std::string& rtrim( std::string& s ) {
51  s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() );
52  return s;
53  }
54  // trim from both ends
55  static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); }
56 } // namespace
57 
58 namespace {
62  inline void factoryInfoSetHelper( std::string& dest, const std::string& value, const std::string& desc,
63  const std::string& id ) {
64  if ( dest.empty() ) {
65  dest = value;
66  } else if ( dest != value ) {
67  Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc +
68  ": " + dest + " != " + value );
69  }
70  }
71 
72  struct OldStyleCnv {
74  void operator()( const char c ) {
75  switch ( c ) {
76  case '<':
77  case '>':
78  case ',':
79  case '(':
80  case ')':
81  case ':':
82  case '.':
83  name.push_back( '_' );
84  break;
85  case '&':
86  name.push_back( 'r' );
87  break;
88  case '*':
89  name.push_back( 'p' );
90  break;
91  case ' ':
92  break;
93  default:
94  name.push_back( c );
95  break;
96  }
97  }
98  };
100  std::string old_style_name( const std::string& name ) {
101  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
102  }
103 } // namespace
104 
105 namespace Gaudi {
106  namespace PluginService {
108  Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {}
110  const char* Exception::what() const throw() { return m_msg.c_str(); }
111 
112  namespace Details {
113  void* getCreator( const std::string& id, const std::string& type ) {
114  return Registry::instance().get( id, type );
115  }
116 
118  int status;
120  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
121  if ( !realname ) return id;
122 #if _GLIBCXX_USE_CXX11_ABI
123  return std::regex_replace(
124  realname.get(),
125  std::regex{ "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?" },
126  "std::string" );
127 #else
128  return std::string{ realname.get() };
129 #endif
130  }
131  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
132 
134  auto _guard = std::scoped_lock{ ::registrySingletonMutex };
135  static Registry r;
136  return r;
137  }
138 
139  Registry::Registry() : m_initialized( false ) {}
140 
142  auto _guard = std::scoped_lock{ m_mutex };
143  if ( m_initialized ) return;
144  m_initialized = true;
145 #if defined( _WIN32 )
146  const char* envVar = "PATH";
147  const char sep = ';';
148 #else
149  const char* envVar = "LD_LIBRARY_PATH";
150  const char sep = ':';
151 #endif
152  char* search_path = ::getenv( envVar );
153  if ( search_path ) {
154  logger().debug( std::string( "searching factories in " ) + envVar );
155  std::string path( search_path );
156  std::string::size_type pos = 0;
157  std::string::size_type newpos = 0;
158  while ( pos != std::string::npos ) {
159  std::string dirName;
160  // get the next entry in the path
161  newpos = path.find( sep, pos );
162  if ( newpos != std::string::npos ) {
163  dirName = path.substr( pos, newpos - pos );
164  pos = newpos + 1;
165  } else {
166  dirName = path.substr( pos );
167  pos = newpos;
168  }
169  logger().debug( std::string( " looking into " ) + dirName );
170  // look for files called "*.components" in the directory
171  DIR* dir = opendir( dirName.c_str() );
172  if ( dir ) {
173  struct dirent* entry;
174  while ( ( entry = readdir( dir ) ) ) {
175  std::string name( entry->d_name );
176  // check if the file name ends with ".components"
177  std::string::size_type extpos = name.find( ".components" );
178  if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
179  std::string fullPath = ( dirName + '/' + name );
180  { // check if it is a regular file
181  struct stat buf;
182  stat( fullPath.c_str(), &buf );
183  if ( !S_ISREG( buf.st_mode ) ) continue;
184  }
185  // read the file
186  logger().debug( std::string( " reading " ) + name );
187  std::ifstream factories{ fullPath };
189  int factoriesCount = 0;
190  int lineCount = 0;
191  while ( !factories.eof() ) {
192  ++lineCount;
194  trim( line );
195  // skip empty lines and lines starting with '#'
196  if ( line.empty() || line[0] == '#' ) continue;
197  // only accept "v1" factories
198  if ( line.substr( 0, 4 ) == "v1::" )
199  line = line.substr( 4 );
200  else
201  continue;
202  // look for the separator
203  auto pos = line.find( ':' );
204  if ( pos == std::string::npos ) {
205  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
206  continue;
207  }
208  const std::string lib( line, 0, pos );
209  const std::string fact( line, pos + 1 );
210  m_factories.emplace( fact, FactoryInfo( lib ) );
211 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
212  // add an alias for the factory using the Reflex convention
213  std::string old_name = old_style_name( fact );
214  if ( fact != old_name ) {
215  FactoryInfo old_info( lib );
216  old_info.properties["ReflexName"] = "true";
217  m_factories.emplace( old_name, old_info );
218  }
219 #endif
220  ++factoriesCount;
221  }
222  if ( logger().level() <= Logger::Debug ) {
223  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
224  }
225  }
226  }
227  closedir( dir );
228  }
229  }
230  }
231  }
232 
233  Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
234  const std::string& rtype, const std::string& className,
235  const Properties& props ) {
236  auto _guard = std::scoped_lock{ m_mutex };
237  FactoryMap& facts = factories();
238  auto entry = facts.find( id );
239  if ( entry == facts.end() ) {
240  // this factory was not known yet
241  entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
242  } else {
243  // do not replace an existing factory with a new one
244  if ( !entry->second.ptr ) entry->second.ptr = factory;
245  factoryInfoSetHelper( entry->second.type, type, "type", id );
246  factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
247  factoryInfoSetHelper( entry->second.className, className, "class", id );
248  }
249 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
250  // add an alias for the factory using the Reflex convention
251  std::string old_name = old_style_name( id );
252  if ( id != old_name )
253  add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
254 #endif
255  return entry->second;
256  }
257 
258  void* Registry::get( const std::string& id, const std::string& type ) const {
259  auto _guard = std::scoped_lock{ m_mutex };
260  const FactoryMap& facts = factories();
261  auto f = facts.find( id );
262  if ( f != facts.end() ) {
263 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
264  const Properties& props = f->second.properties;
265  if ( props.find( "ReflexName" ) != props.end() )
266  logger().warning( "requesting factory via old name '" + id +
267  "'"
268  "use '" +
269  f->second.className + "' instead" );
270 #endif
271  if ( !f->second.ptr ) {
272  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
273  logger().warning( "cannot load " + f->second.library + " for factory " + id );
274  char* dlmsg = dlerror();
275  if ( dlmsg ) logger().warning( dlmsg );
276  return nullptr;
277  }
278  f = facts.find( id ); // ensure that the iterator is valid
279  }
280  if ( f->second.type == type ) return f->second.ptr;
281  logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
282  " instead of " + demangle( type ) );
283  }
284  return nullptr; // factory not found
285  }
286 
288  auto _guard = std::scoped_lock{ m_mutex };
289  static const FactoryInfo unknown( "unknown" );
290  const FactoryMap& facts = factories();
291  auto f = facts.find( id );
292  return ( f != facts.end() ) ? f->second : unknown;
293  }
294 
296  auto _guard = std::scoped_lock{ m_mutex };
297  FactoryMap& facts = factories();
298  auto f = facts.find( id );
299  if ( f != facts.end() ) f->second.properties[k] = v;
300  return *this;
301  }
302 
304  auto _guard = std::scoped_lock{ m_mutex };
306  for ( const auto& f : factories() ) {
307  if ( f.second.ptr ) l.insert( f.first );
308  }
309  return l;
310  }
311 
312  void Logger::report( Level lvl, const std::string& msg ) {
313  static const char* levels[] = { "DEBUG : ", "INFO : ", "WARNING: ", "ERROR : " };
314  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
315  }
316 
317  static auto s_logger = std::make_unique<Logger>();
318  Logger& logger() { return *s_logger; }
319  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
320 
321  } // namespace Details
322 
323  void SetDebug( int debugLevel ) {
324  using namespace Details;
325  Logger& l = logger();
326  if ( debugLevel > 1 )
327  l.setLevel( Logger::Debug );
328  else if ( debugLevel > 0 )
329  l.setLevel( Logger::Info );
330  else
331  l.setLevel( Logger::Warning );
332  }
333 
334  int Debug() {
335  using namespace Details;
336  switch ( logger().level() ) {
337  case Logger::Debug:
338  return 2;
339  case Logger::Info:
340  return 1;
341  default:
342  return 0;
343  }
344  }
345  }
346  } // namespace PluginService
347 } // namespace Gaudi
std::for_each
T for_each(T... args)
Gaudi::PluginService::v1::Details::Registry
In-memory database of the loaded factories.
Definition: PluginServiceDetailsV1.h:83
std::string
STL class.
AtlasMCRecoFullPrecedenceDump.path
path
Definition: AtlasMCRecoFullPrecedenceDump.py:49
GaudiPartProp.tests.v1
v1
Definition: tests.py:39
gaudirun.s
string s
Definition: gaudirun.py:346
std::find_if_not
T find_if_not(T... args)
std::type_info
check_ParticleID.props
props
Definition: check_ParticleID.py:21
Gaudi::PluginService::v1::Details::Registry::m_mutex
std::recursive_mutex m_mutex
Mutex used to control concurrent access to the internal data.
Definition: PluginServiceDetailsV1.h:182
Gaudi::PluginService::v1::Details::Logger::debug
void debug(const std::string &msg)
Definition: PluginServiceDetailsV1.h:194
Gaudi::PluginService::v1::Details::getCreator
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Function used to load a specific factory function.
Definition: PluginServiceV1.cpp:113
Gaudi::PluginService::v1::Details::Registry::Registry
Registry()
Private constructor for the singleton pattern.
Definition: PluginServiceV1.cpp:139
GaudiMP.FdsRegistry.msg
msg
Definition: FdsRegistry.py:19
std::map::emplace
T emplace(T... args)
Gaudi::PluginService::v1::Exception::Exception
Exception(std::string msg)
Definition: PluginServiceV1.cpp:108
gaudirun.c
c
Definition: gaudirun.py:525
GaudiPartProp.tests.id
id
Definition: tests.py:111
Gaudi::PluginService::v1::Details::Registry::instance
static Registry & instance()
Retrieve the singleton instance of Registry.
Definition: PluginServiceV1.cpp:133
Gaudi::PluginService::v1::Details::Registry::m_factories
FactoryMap m_factories
Internal storage for factories.
Definition: PluginServiceDetailsV1.h:179
Gaudi::PluginService::v1::Details::Registry::m_initialized
bool m_initialized
Flag recording if the registry has been initialized or not.
Definition: PluginServiceDetailsV1.h:176
Gaudi::PluginService::v1::Details::Logger::Info
@ Info
Definition: PluginServiceDetailsV1.h:188
Properties
Definition: Properties.py:1
PluginService.h
std::cerr
Gaudi::PluginService::v1::Details::setLogger
GAUDIPS_API void setLogger(Logger *logger)
Set the logger instance to use.
Definition: PluginServiceV1.cpp:319
Gaudi::PluginService::v1::Details::Registry::getInfo
const FactoryInfo & getInfo(const std::string &id) const
Retrieve the FactoryInfo object for an id.
Definition: PluginServiceV1.cpp:287
std::string::c_str
T c_str(T... args)
Gaudi::PluginService::v1::Details::Registry::initialize
void initialize()
Initialize the registry loading the list of factories from the .component files in the library search...
Definition: PluginServiceV1.cpp:141
std::to_string
T to_string(T... args)
Gaudi::PluginService::v1::Details::Logger::Debug
@ Debug
Definition: PluginServiceDetailsV1.h:188
Gaudi::PluginService::v1::Details::Registry::FactoryInfo::properties
Properties properties
Definition: PluginServiceDetailsV1.h:105
std::map< KeyType, FactoryInfo >
Gaudi::PluginService::v1::Details::Logger::report
virtual void report(Level lvl, const std::string &msg)
Definition: PluginServiceV1.cpp:312
Gaudi::PluginService::v1::Details::Registry::factories
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
Definition: PluginServiceDetailsV1.h:146
std::regex
gaudirun.level
level
Definition: gaudirun.py:364
Gaudi
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition: __init__.py:1
Gaudi::PluginService::v1::Details::Logger::level
Level level() const
Definition: PluginServiceDetailsV1.h:191
Gaudi::PluginService::v1::Exception::what
const char * what() const override
Definition: PluginServiceV1.cpp:110
Gaudi::PluginService::v1::SetDebug
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
Definition: PluginServiceV1.cpp:323
GAUDI_PLUGIN_SERVICE_V1_INLINE
#define GAUDI_PLUGIN_SERVICE_V1_INLINE
Definition: PluginServiceCommon.h:18
Gaudi::PluginService::v1::Details::Registry::add
FactoryInfo & add(const I &id, typename F::FuncType ptr)
Add a factory to the database.
Definition: PluginServiceDetailsV1.h:121
gaudirun.dest
dest
Definition: gaudirun.py:224
gaudirun.type
type
Definition: gaudirun.py:160
Gaudi::PluginService::v1::Details::logger
GAUDIPS_API Logger & logger()
Return the current logger instance.
Definition: PluginServiceV1.cpp:318
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
std::endl
T endl(T... args)
gaudirun.l
dictionary l
Definition: gaudirun.py:583
Gaudi::PluginService::v1::Exception::m_msg
std::string m_msg
Definition: PluginServiceV1.h:80
std::getline
T getline(T... args)
std
STL namespace.
Gaudi::PluginService::v1::Details::Registry::addProperty
Registry & addProperty(const std::string &id, const std::string &k, const std::string &v)
Add a property to an already existing FactoryInfo object (via its id.)
Definition: PluginServiceV1.cpp:295
std::isspace
T isspace(T... args)
Gaudi::PluginService::v1::Details::Logger::Level
Level
Definition: PluginServiceDetailsV1.h:188
plotSpeedupsPyRoot.line
line
Definition: plotSpeedupsPyRoot.py:198
Properties.v
v
Definition: Properties.py:122
Gaudi::PluginService::v1::Debug
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
Definition: PluginServiceV1.cpp:334
std::mutex
STL class.
Gaudi::PluginService::v1::Details::Registry::FactoryInfo::ptr
void * ptr
Definition: PluginServiceDetailsV1.h:101
std::map::end
T end(T... args)
Gaudi::PluginService::v1::Details::Registry::FactoryInfo
Definition: PluginServiceDetailsV1.h:90
Gaudi::PluginService::v1::Details::demangle
GAUDIPS_API std::string demangle(const std::type_info &id)
Return a canonical name for type_info object (implementation borrowed from GaudiKernel/System).
Definition: PluginServiceV1.cpp:131
Gaudi::PluginService::v1::Exception::~Exception
~Exception() override
Definition: PluginServiceV1.cpp:109
std::unique_ptr
STL class.
Gaudi::PluginService::v1::Details::Logger
Simple logging class, just to provide a default implementation.
Definition: PluginServiceDetailsV1.h:186
Gaudi::PluginService::v1::Details::Logger::Warning
@ Warning
Definition: PluginServiceDetailsV1.h:188
std::regex_replace
T regex_replace(T... args)
Gaudi::PluginService::v1::Details::Logger::warning
void warning(const std::string &msg)
Definition: PluginServiceDetailsV1.h:195
std::set
STL class.
Gaudi::PluginService::v1::Details::Registry::loadedFactoryNames
std::set< KeyType > loadedFactoryNames() const
Return a list of all the known and loaded factories.
Definition: PluginServiceV1.cpp:303
Gaudi::PluginService::v1::Details::Registry::get
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
Definition: PluginServiceV1.cpp:258
std::ifstream
STL class.