forked from nest/nest-simulator
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdynamicloader.cpp
308 lines (253 loc) · 8.8 KB
/
dynamicloader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*
* dynamicloader.cpp
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* NEST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
This file is part of NEST.
dynamicloader.cpp -- Implements the class DynamicLoaderModule
to allow for dymanically loaded modules for extending the kernel.
Author(s):
Moritz Helias
First Version: November 2005
*/
#include "dynamicloader.h"
#ifdef HAVE_LIBLTDL
// External includes:
#include <ltdl.h>
// Includes from libnestutil:
#include "logging.h"
// Includes from nestkernel:
#include "kernel_manager.h"
#include "model.h"
// Includes from sli:
#include "integerdatum.h"
#include "interpret.h"
#include "stringdatum.h"
namespace nest
{
struct sDynModule
{
std::string name;
lt_dlhandle handle;
SLIModule* pModule;
bool
operator==( const sDynModule& rhs ) const
{
return name == rhs.name;
}
// operator!= must be implemented explicitly, not all compilers
// generate it automatically from operator==
bool
operator!=( const sDynModule& rhs ) const
{
return not( *this == rhs );
}
};
// static member initialization
Dictionary* DynamicLoaderModule::moduledict_ = new Dictionary();
vecLinkedModules&
DynamicLoaderModule::getLinkedModules()
{
static vecLinkedModules lm; // initialized empty on first call
return lm;
}
/*! At the time when DynamicLoaderModule is constructed, the SLI Interpreter
and NestModule must be already constructed and initialized.
DynamicLoaderModule relies on the presence of
the following SLI datastructures: Name, Dictionary.
*/
DynamicLoaderModule::DynamicLoaderModule( SLIInterpreter& interpreter )
: dyn_modules()
, loadmodule_function( dyn_modules )
{
interpreter.def( "moduledict", new DictionaryDatum( moduledict_ ) );
}
DynamicLoaderModule::~DynamicLoaderModule()
{
// unload all loaded modules
for ( vecDynModules::iterator it = dyn_modules.begin(); it != dyn_modules.end(); ++it )
{
if ( it->handle )
{
lt_dlclose( it->handle );
it->handle = nullptr;
}
}
lt_dlexit();
}
// The following concerns the new module: -----------------------
const std::string
DynamicLoaderModule::name() const
{
return std::string( "NEST-Dynamic Loader" ); // Return name of the module
}
const std::string
DynamicLoaderModule::commandstring() const
{
return std::string( "" ); // Run associated SLI startup script
}
// auxiliary function to check name of module via its pointer
// we cannot use a & for the second argument, as std::bind2nd() then
// becomes confused, at least with g++ 4.0.1.
bool
has_name( SLIModule const* const m, const std::string n )
{
return m->name() == n;
}
/** @BeginDocumentation
Name: Install - Load a dynamic module to extend the functionality.
Description:
Synopsis: (module_name) Install -> handle
*/
DynamicLoaderModule::LoadModuleFunction::LoadModuleFunction( vecDynModules& dyn_modules )
: dyn_modules_( dyn_modules )
{
}
void
DynamicLoaderModule::LoadModuleFunction::execute( SLIInterpreter* i ) const
{
i->assert_stack_load( 1 );
sDynModule new_module;
new_module.name = getValue< std::string >( i->OStack.top() );
if ( new_module.name.empty() )
{
throw DynamicModuleManagementError( "Module name must not be empty." );
}
// check if module already loaded
// this check can happen here, since we are comparing dynamically loaded
// modules based on the name given to the Install command
if ( std::find( dyn_modules_.begin(), dyn_modules_.end(), new_module ) != dyn_modules_.end() )
{
throw DynamicModuleManagementError( "Module '" + new_module.name + "' is loaded already." );
}
// call lt_dlerror() to reset any error messages hanging around
lt_dlerror();
// try to open the module
const lt_dlhandle hModule = lt_dlopenext( new_module.name.c_str() );
if ( not hModule )
{
char* errstr = ( char* ) lt_dlerror();
std::string msg = "Module '" + new_module.name + "' could not be opened.";
if ( errstr )
{
msg += "\nThe dynamic loader returned the following error: '" + std::string( errstr ) + "'.";
}
msg += "\n\nPlease check LD_LIBRARY_PATH (OSX: DYLD_LIBRARY_PATH)!";
throw DynamicModuleManagementError( msg );
}
// see if we can find the mod symbol in the module
SLIModule* pModule = ( SLIModule* ) lt_dlsym( hModule, "mod" );
char* errstr = ( char* ) lt_dlerror();
if ( errstr )
{
lt_dlclose( hModule ); // close module again
lt_dlerror(); // remove any error caused by lt_dlclose()
throw DynamicModuleManagementError(
"Module '" + new_module.name + "' could not be loaded.\n"
"The dynamic loader returned the following error: '"
+ std::string(errstr) + "'.");
}
// check if module is linked in. This test is based on the module name
// returned by DynModule::name(), since we have no file names for linked
// modules. We can only perform it after we have loaded the module.
if ( std::find_if( DynamicLoaderModule::getLinkedModules().begin(),
DynamicLoaderModule::getLinkedModules().end(),
std::bind( has_name, std::placeholders::_1, pModule->name() ) )
!= DynamicLoaderModule::getLinkedModules().end() )
{
lt_dlclose( hModule ); // close module again
lt_dlerror(); // remove any error caused by lt_dlclose()
throw DynamicModuleManagementError(
"Module '" + new_module.name + "' is linked into NEST.\n"
"You neither need nor may load it dynamically in addition.");
}
// all is well an we can register the module with the interpreter
try
{
pModule->install( std::cerr, i );
}
catch ( std::exception& e )
{
// We should uninstall the partially installed module here, but
// this must wait for #152.
// For now, we just close the module file and rethrow the exception.
lt_dlclose( hModule );
lt_dlerror(); // remove any error caused by lt_dlclose()
throw; // no arg re-throws entire exception, see Stroustrup 14.3.1
}
// add the handle to list of loaded modules
new_module.handle = hModule;
new_module.pModule = pModule;
dyn_modules_.push_back( new_module );
LOG( M_INFO, "Install", ( "loaded module " + pModule->name() ).c_str() );
// remove operand and operator from stack
i->OStack.pop();
i->EStack.pop();
// put handle to module onto stack
int moduleid = dyn_modules_.size() - 1;
i->OStack.push( moduleid );
( *moduledict_ )[ new_module.name ] = moduleid;
// now we can run the module initializer, after we have cleared the EStack
if ( not pModule->commandstring().empty() )
{
Token t = new StringDatum( pModule->commandstring() );
i->OStack.push_move( t );
Token c = new NameDatum( "initialize_module" );
i->EStack.push_move( c );
}
}
void
DynamicLoaderModule::init( SLIInterpreter* i )
{
// bind functions to terminal names
i->createcommand( "Install", &loadmodule_function );
// the ld_* functions return 0 on success and an int > 0 on failure
if ( lt_dlinit() )
{
LOG( M_ERROR, "DynamicLoaderModule::init", "Could not initialize libltdl. No dynamic modules will be available." );
}
// To avoid problems due to string substitution in NEST binaries during
// Conda installation, we need to convert the literal to string, cstr and back,
// see #2237 and https://github.com/conda/conda-build/issues/1674#issuecomment-280378336
const std::string module_dir = std::string( NEST_INSTALL_PREFIX ).c_str() + std::string( "/" NEST_INSTALL_LIBDIR );
if ( lt_dladdsearchdir( module_dir.c_str() ) )
{
LOG( M_ERROR, "DynamicLoaderModule::init", "Could not add dynamic module search directory." );
}
}
int
DynamicLoaderModule::registerLinkedModule( SLIModule* pModule )
{
assert( pModule );
getLinkedModules().push_back( pModule );
return getLinkedModules().size();
}
void
DynamicLoaderModule::initLinkedModules( SLIInterpreter& interpreter )
{
for ( vecLinkedModules::iterator it = getLinkedModules().begin(); it != getLinkedModules().end(); ++it )
{
interpreter.message( SLIInterpreter::M_STATUS, "DynamicLoaderModule::initLinkedModules", "adding linked module" );
interpreter.message( SLIInterpreter::M_STATUS, "DynamicLoaderModule::initLinkedModules", ( *it )->name().c_str() );
interpreter.addlinkedusermodule( *it );
}
}
} // namespace nest
#endif // HAVE_LIBLTDL