| The Gaudi Framework v32r1 (f65d50dc)

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