Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "PlumedMainInitializer.h"
23 : #include "PlumedMain.h"
24 : #include "tools/Exception.h"
25 : #include "lepton/Exception.h"
26 : #include <cstdlib>
27 : #include <cstring>
28 : #include <iostream>
29 : #if defined __PLUMED_HAS_DLOPEN
30 : #include <dlfcn.h>
31 : #endif
32 : #include <exception>
33 : #include <stdexcept>
34 : #include <ios>
35 : #include <new>
36 : #include <typeinfo>
37 : #ifdef __PLUMED_LIBCXX11
38 : #include <system_error>
39 : #include <future>
40 : #include <memory>
41 : #include <functional>
42 : #endif
43 : #include "tools/TypesafePtr.h"
44 : #include "tools/Log.h"
45 : #include "tools/Tools.h"
46 :
47 :
48 16180 : static bool getenvTypesafeDebug() noexcept {
49 16180 : static const auto* res=std::getenv("PLUMED_TYPESAFE_DEBUG");
50 16180 : return res;
51 : }
52 :
53 0 : static void typesafeDebug(const char*key,plumed_safeptr_x safe) noexcept {
54 0 : std::fprintf(stderr,"+++ PLUMED_TYPESAFE_DEBUG %s %p %zu",key,safe.ptr,safe.nelem);
55 0 : const size_t* shape=safe.shape;
56 0 : if(shape) {
57 0 : std::fprintf(stderr," (");
58 0 : while(*shape!=0) {
59 0 : std::fprintf(stderr," %zu",*shape);
60 0 : shape++;
61 : }
62 0 : std::fprintf(stderr," )");
63 : }
64 0 : std::fprintf(stderr," %zx %p\n",safe.flags,safe.opt);
65 0 : }
66 :
67 : // create should never throw
68 : // in case of a problem, it logs the error and return a null pointer
69 : // when loaded by an interface >=2.5, this will result in a non valid plumed object.
70 : // earlier interfaces will just give a segfault or a failed assertion.
71 804781 : extern "C" void*plumed_plumedmain_create() {
72 : try {
73 804781 : return new PLMD::PlumedMain;
74 0 : } catch(const std::exception & e) {
75 0 : std::cerr<<"+++ an error happened while creating a plumed object\n";
76 0 : std::cerr<<e.what()<<std::endl;
77 : return nullptr;
78 0 : } catch(...) {
79 0 : std::cerr<<"+++ an unknown error happened while creating a plumed object"<<std::endl;
80 : return nullptr;
81 0 : }
82 : }
83 :
84 8000075 : extern "C" unsigned plumed_plumedmain_create_reference(void*plumed) {
85 8000075 : plumed_massert(plumed,"trying to create a reference to a plumed object which is not initialized");
86 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
87 8000075 : return p->increaseReferenceCounter();
88 : }
89 :
90 8804856 : extern "C" unsigned plumed_plumedmain_delete_reference(void*plumed) {
91 8804856 : plumed_massert(plumed,"trying to delete a reference to a plumed object which is not initialized");
92 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
93 8804856 : return p->decreaseReferenceCounter();
94 : }
95 :
96 42 : extern "C" unsigned plumed_plumedmain_use_count(void*plumed) {
97 42 : plumed_massert(plumed,"trying to delete a reference to a plumed object which is not initialized");
98 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
99 42 : return p->useCountReferenceCounter();
100 : }
101 :
102 369 : extern "C" void plumed_plumedmain_cmd(void*plumed,const char*key,const void*val) {
103 369 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
104 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
105 738 : p->cmd(key,PLMD::TypesafePtr::unchecked(val));
106 369 : }
107 :
108 : extern "C" {
109 107 : static void plumed_plumedmain_cmd_safe(void*plumed,const char*key,plumed_safeptr_x safe) {
110 107 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
111 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
112 107 : if(getenvTypesafeDebug()) {
113 0 : typesafeDebug(key,safe);
114 : }
115 214 : p->cmd(key,PLMD::TypesafePtr::fromSafePtr(&safe));
116 107 : }
117 : }
118 :
119 : /// Internal tool
120 : /// Throws the currently managed exception and call the nothrow handler.
121 : /// If nested is not null, it is passed and then gets populated with a pointer that should
122 : /// be called on the nested exception
123 : /// If msg is not null, it overrides the message. Can be used to build a concatenated message.
124 111 : static void translate_current(plumed_nothrow_handler_x nothrow,void**nested=nullptr,const char*msg=nullptr) {
125 111 : const void* opt[5]= {"n",nested,nullptr,nullptr,nullptr};
126 : try {
127 : // this function needs to be called while catching an exception
128 : // cppcheck-suppress rethrowNoCurrentException
129 111 : throw;
130 111 : } catch(const PLMD::ExceptionTypeError & e) {
131 8 : if(!msg) {
132 0 : msg=e.what();
133 : }
134 8 : nothrow.handler(nothrow.ptr,20300,msg,opt);
135 63 : } catch(const PLMD::ExceptionError & e) {
136 55 : if(!msg) {
137 2 : msg=e.what();
138 : }
139 55 : nothrow.handler(nothrow.ptr,20200,msg,opt);
140 56 : } catch(const PLMD::ExceptionDebug & e) {
141 1 : if(!msg) {
142 1 : msg=e.what();
143 : }
144 1 : nothrow.handler(nothrow.ptr,20100,msg,opt);
145 22 : } catch(const PLMD::Exception & e) {
146 21 : if(!msg) {
147 16 : msg=e.what();
148 : }
149 21 : nothrow.handler(nothrow.ptr,20000,msg,opt);
150 22 : } catch(const PLMD::lepton::Exception & e) {
151 1 : if(!msg) {
152 1 : msg=e.what();
153 : }
154 1 : nothrow.handler(nothrow.ptr,19900,msg,opt);
155 : // 11000 to 12000 are "bad exceptions". message will be copied without new allocations
156 2 : } catch(const std::bad_exception & e) {
157 1 : if(!msg) {
158 1 : msg=e.what();
159 : }
160 1 : nothrow.handler(nothrow.ptr,11500,msg,opt);
161 : #ifdef __PLUMED_LIBCXX11
162 2 : } catch(const std::bad_array_new_length & e) {
163 1 : if(!msg) {
164 1 : msg=e.what();
165 : }
166 1 : nothrow.handler(nothrow.ptr,11410,msg,opt);
167 : #endif
168 4 : } catch(const std::bad_alloc & e) {
169 3 : if(!msg) {
170 3 : msg=e.what();
171 : }
172 3 : nothrow.handler(nothrow.ptr,11400,msg,opt);
173 : #ifdef __PLUMED_LIBCXX11
174 4 : } catch(const std::bad_function_call & e) {
175 1 : if(!msg) {
176 1 : msg=e.what();
177 : }
178 1 : nothrow.handler(nothrow.ptr,11300,msg,opt);
179 2 : } catch(const std::bad_weak_ptr & e) {
180 1 : if(!msg) {
181 1 : msg=e.what();
182 : }
183 1 : nothrow.handler(nothrow.ptr,11200,msg,opt);
184 : #endif
185 2 : } catch(const std::bad_cast & e) {
186 1 : if(!msg) {
187 1 : msg=e.what();
188 : }
189 1 : nothrow.handler(nothrow.ptr,11100,msg,opt);
190 2 : } catch(const std::bad_typeid & e) {
191 1 : if(!msg) {
192 1 : msg=e.what();
193 : }
194 1 : nothrow.handler(nothrow.ptr,11000,msg,opt);
195 : // not implemented yet: std::regex_error
196 : // we do not allow regex yet due to portability problems with gcc 4.8
197 : // as soon as we transition to using <regex> it should be straightforward to add
198 2 : } catch(const std::ios_base::failure & e) {
199 1 : if(!msg) {
200 1 : msg=e.what();
201 : }
202 : #ifdef __PLUMED_LIBCXX11
203 1 : int value=e.code().value();
204 1 : opt[2]="c"; // "c" passes the error code.
205 1 : opt[3]=&value;
206 1 : if(e.code().category()==std::generic_category()) {
207 0 : nothrow.handler(nothrow.ptr,10230,msg,opt);
208 1 : } else if(e.code().category()==std::system_category()) {
209 0 : nothrow.handler(nothrow.ptr,10231,msg,opt);
210 1 : } else if(e.code().category()==std::iostream_category()) {
211 1 : nothrow.handler(nothrow.ptr,10232,msg,opt);
212 0 : } else if(e.code().category()==std::future_category()) {
213 0 : nothrow.handler(nothrow.ptr,10233,msg,opt);
214 : } else
215 : #endif
216 : // 10239 represents std::ios_base::failure with default constructur
217 0 : nothrow.handler(nothrow.ptr,10239,msg,opt);
218 : #ifdef __PLUMED_LIBCXX11
219 5 : } catch(const std::system_error & e) {
220 4 : if(!msg) {
221 4 : msg=e.what();
222 : }
223 4 : int value=e.code().value();
224 4 : opt[2]="c"; // "c" passes the error code.
225 4 : opt[3]=&value;
226 4 : if(e.code().category()==std::generic_category()) {
227 1 : nothrow.handler(nothrow.ptr,10220,msg,opt);
228 3 : } else if(e.code().category()==std::system_category()) {
229 1 : nothrow.handler(nothrow.ptr,10221,msg,opt);
230 2 : } else if(e.code().category()==std::iostream_category()) {
231 1 : nothrow.handler(nothrow.ptr,10222,msg,opt);
232 1 : } else if(e.code().category()==std::future_category()) {
233 1 : nothrow.handler(nothrow.ptr,10223,msg,opt);
234 : }
235 : // fallback to generic runtime_error
236 : else {
237 0 : nothrow.handler(nothrow.ptr,10200,msg,opt);
238 : }
239 : #endif
240 5 : } catch(const std::underflow_error &e) {
241 1 : if(!msg) {
242 1 : msg=e.what();
243 : }
244 1 : nothrow.handler(nothrow.ptr,10215,msg,opt);
245 2 : } catch(const std::overflow_error &e) {
246 1 : if(!msg) {
247 1 : msg=e.what();
248 : }
249 1 : nothrow.handler(nothrow.ptr,10210,msg,opt);
250 2 : } catch(const std::range_error &e) {
251 1 : if(!msg) {
252 1 : msg=e.what();
253 : }
254 1 : nothrow.handler(nothrow.ptr,10205,msg,opt);
255 2 : } catch(const std::runtime_error & e) {
256 1 : if(!msg) {
257 1 : msg=e.what();
258 : }
259 1 : nothrow.handler(nothrow.ptr,10200,msg,opt);
260 : // not implemented yet: std::future_error
261 : // not clear how useful it would be.
262 2 : } catch(const std::out_of_range & e) {
263 1 : if(!msg) {
264 1 : msg=e.what();
265 : }
266 1 : nothrow.handler(nothrow.ptr,10120,msg,opt);
267 2 : } catch(const std::length_error & e) {
268 1 : if(!msg) {
269 1 : msg=e.what();
270 : }
271 1 : nothrow.handler(nothrow.ptr,10115,msg,opt);
272 2 : } catch(const std::domain_error & e) {
273 1 : if(!msg) {
274 1 : msg=e.what();
275 : }
276 1 : nothrow.handler(nothrow.ptr,10110,msg,opt);
277 2 : } catch(const std::invalid_argument & e) {
278 1 : if(!msg) {
279 1 : msg=e.what();
280 : }
281 1 : nothrow.handler(nothrow.ptr,10105,msg,opt);
282 2 : } catch(const std::logic_error & e) {
283 1 : if(!msg) {
284 1 : msg=e.what();
285 : }
286 1 : nothrow.handler(nothrow.ptr,10100,msg,opt);
287 : // generic exception. message will be copied without new allocations
288 : // reports all non caught exceptions that are derived from std::exception
289 : // for instance, boost exceptions would end up here
290 1 : } catch(const std::exception & e) {
291 0 : if(!msg) {
292 0 : msg=e.what();
293 : }
294 0 : nothrow.handler(nothrow.ptr,10000,msg,opt);
295 1 : } catch(const char* m) {
296 1 : if(!msg) {
297 : msg=m;
298 : }
299 1 : nothrow.handler(nothrow.ptr,10000,msg,opt);
300 1 : } catch(const std::string & s) {
301 0 : if(!msg) {
302 : msg=s.c_str();
303 : }
304 0 : nothrow.handler(nothrow.ptr,10000,msg,opt);
305 1 : } catch (...) {
306 : // if exception cannot be translated, we add a bad_exception to the stack
307 1 : nothrow.handler(nothrow.ptr,11500,"plumed could not translate exception",opt);
308 1 : }
309 111 : }
310 :
311 45 : static void translate_nested(plumed_nothrow_handler_x nothrow) {
312 : try {
313 45 : throw;
314 45 : } catch (const std::nested_exception & e) {
315 : // If this exception has a nested one:
316 12 : auto nothrow_nested=nothrow;
317 12 : nothrow_nested.ptr=nullptr;
318 : // translate the current exception asking the wrapper to allocate a new exception
319 12 : translate_current(nothrow,¬hrow_nested.ptr);
320 : // if the wrapper cannot allocate the exception, this will be a nullptr
321 12 : if(nothrow_nested.ptr) {
322 : try {
323 : // transfer control to the nested exception
324 12 : e.rethrow_nested();
325 12 : } catch (...) {
326 : // recursively translate it
327 12 : translate_nested(nothrow_nested);
328 12 : }
329 : }
330 45 : } catch (...) {
331 : // otherwise, just translate the current exception
332 33 : translate_current(nothrow);
333 33 : }
334 45 : }
335 :
336 : extern "C" {
337 16073 : static void plumed_plumedmain_cmd_safe_nothrow(void*plumed,const char*key,plumed_safeptr_x safe,plumed_nothrow_handler_x nothrow) {
338 : // This is a workaround for a suboptimal choice in PLUMED <2.8
339 : // In particular, the only way to bypass the exception handling process was to call the plumed_plumedmain_cmd_safe
340 : // function directly.
341 : // With this modification, it is possible to just call the plumed_plumedmain_cmd_safe_nothrow function
342 : // passing a null error handler.
343 16073 : if(!nothrow.handler) {
344 0 : plumed_plumedmain_cmd_safe(plumed,key,safe);
345 0 : return;
346 : }
347 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
348 : // At library boundaries we translate exceptions to error codes.
349 : // This allows an exception to be catched also if the MD code
350 : // was linked against a different C++ library
351 : try {
352 16073 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
353 16073 : if(getenvTypesafeDebug()) {
354 0 : typesafeDebug(key,safe);
355 : }
356 32146 : p->cmd(key,PLMD::TypesafePtr::fromSafePtr(&safe));
357 99 : } catch(...) {
358 99 : if(p->getNestedExceptions()) {
359 33 : translate_nested(nothrow);
360 : } else {
361 : // In this case, we just consider the latest thrown exception and
362 : // supplement it with a concatenated message so as not to
363 : // loose information.
364 66 : auto msg=PLMD::Tools::concatenateExceptionMessages();
365 66 : translate_current(nothrow,nullptr,msg.c_str());
366 : }
367 99 : }
368 : }
369 : }
370 :
371 : extern "C" {
372 0 : static void plumed_plumedmain_cmd_nothrow(void*plumed,const char*key,const void*val,plumed_nothrow_handler_x nothrow) {
373 : plumed_safeptr_x safe;
374 0 : plumed_assert(nothrow.handler) << "Accepting a null pointer here would make the calling code non compatible with plumed 2.5 to 2.7";
375 0 : safe.ptr=val;
376 0 : safe.nelem=0;
377 0 : safe.shape=NULL;
378 0 : safe.flags=0;
379 0 : safe.opt=NULL;
380 0 : plumed_plumedmain_cmd_safe_nothrow(plumed,key,safe,nothrow);
381 0 : }
382 : }
383 :
384 804781 : extern "C" void plumed_plumedmain_finalize(void*plumed) {
385 804781 : plumed_massert(plumed,"trying to deallocate a plumed object which is not initialized");
386 : // I think it is not possible to replace this delete with a smart pointer
387 : // since the ownership of this pointer is in a C structure. GB
388 804781 : delete static_cast<PLMD::PlumedMain*>(plumed);
389 804781 : }
390 :
391 : // values here should be consistent with those in plumed_symbol_table_init !!!!
392 : plumed_symbol_table_type_x plumed_symbol_table= {
393 : 4,
394 : {plumed_plumedmain_create,plumed_plumedmain_cmd,plumed_plumedmain_finalize},
395 : plumed_plumedmain_cmd_nothrow,
396 : plumed_plumedmain_cmd_safe,
397 : plumed_plumedmain_cmd_safe_nothrow,
398 : plumed_plumedmain_create_reference,
399 : plumed_plumedmain_delete_reference,
400 : plumed_plumedmain_use_count
401 : };
402 :
403 : // values here should be consistent with those above !!!!
404 809347 : extern "C" void plumed_symbol_table_init() {
405 809347 : plumed_symbol_table.version=4;
406 809347 : plumed_symbol_table.functions.create=plumed_plumedmain_create;
407 809347 : plumed_symbol_table.functions.cmd=plumed_plumedmain_cmd;
408 809347 : plumed_symbol_table.functions.finalize=plumed_plumedmain_finalize;
409 809347 : plumed_symbol_table.cmd_nothrow=plumed_plumedmain_cmd_nothrow;
410 809347 : plumed_symbol_table.cmd_safe=plumed_plumedmain_cmd_safe;
411 809347 : plumed_symbol_table.cmd_safe_nothrow=plumed_plumedmain_cmd_safe_nothrow;
412 809347 : plumed_symbol_table.create_reference=plumed_plumedmain_create_reference;
413 809347 : plumed_symbol_table.delete_reference=plumed_plumedmain_delete_reference;
414 809347 : plumed_symbol_table.use_count=plumed_plumedmain_use_count;
415 809347 : }
416 :
417 : namespace PLMD {
418 :
419 : #define plumed_convert_fptr(ptr,fptr) { ptr=NULL; std::memcpy(&ptr,&fptr,(sizeof(fptr)>sizeof(ptr)?sizeof(ptr):sizeof(fptr))); }
420 :
421 : /// Static object which registers Plumed.
422 : /// This is a static object which, during its construction at startup,
423 : /// registers the pointers to plumed_plumedmain_create, plumed_plumedmain_cmd and plumed_plumedmain_finalize
424 : /// to the plumed_kernel_register function.
425 : /// Registration is only required with plumed loader <=2.4, but we do it anyway in order to maintain
426 : /// backward compatibility. Notice that as of plumed 2.5 the plumed_kernel_register is found
427 : /// using dlsym, in order to allow the libplumedKernel library to be loadable also when
428 : /// the plumed_kernel_register symbol is not available.
429 : namespace {
430 : class PlumedMainInitializer {
431 : const bool debug;
432 : public:
433 4595 : PlumedMainInitializer():
434 4595 : debug(std::getenv("PLUMED_LOAD_DEBUG")) {
435 : // make sure static plumed_function_pointers is initialized here
436 4595 : plumed_symbol_table_init();
437 4595 : if(debug) {
438 0 : std::fprintf(stderr,"+++ Initializing PLUMED with plumed_symbol_table version %i at %p\n",plumed_symbol_table.version,(void*)&plumed_symbol_table);
439 : }
440 : #if defined(__PLUMED_HAS_DLOPEN)
441 4595 : if(std::getenv("PLUMED_LOAD_SKIP_REGISTRATION")) {
442 0 : if(debug) {
443 0 : std::fprintf(stderr,"+++ Skipping registration +++\n");
444 : }
445 0 : return;
446 : }
447 : typedef plumed_plumedmain_function_holder_x* (*plumed_kernel_register_type_x)(const plumed_plumedmain_function_holder_x*);
448 : plumed_kernel_register_type_x plumed_kernel_register=nullptr;
449 : void* handle=nullptr;
450 : #if defined(__PLUMED_HAS_RTLD_DEFAULT)
451 4595 : if(debug) {
452 0 : std::fprintf(stderr,"+++ Registering functions. Looking in RTLD_DEFAULT +++\n");
453 : }
454 4595 : void* dls=dlsym(RTLD_DEFAULT,"plumed_kernel_register");
455 : #else
456 : handle=dlopen(NULL,RTLD_LOCAL);
457 : if(debug) {
458 : std::fprintf(stderr,"+++ Registering functions. dlopen handle at %p +++\n",handle);
459 : }
460 : void* dls=dlsym(handle,"plumed_kernel_register");
461 : #endif
462 4595 : *(void **)(&plumed_kernel_register)=dls;
463 4595 : if(debug) {
464 0 : if(plumed_kernel_register) {
465 0 : std::fprintf(stderr,"+++ plumed_kernel_register found at %p +++\n",dls);
466 : } else {
467 0 : std::fprintf(stderr,"+++ plumed_kernel_register not found +++\n");
468 : }
469 : }
470 : void*createp;
471 : void*cmdp;
472 : void*finalizep;
473 : plumed_convert_fptr(createp,plumed_symbol_table.functions.create);
474 : plumed_convert_fptr(cmdp,plumed_symbol_table.functions.cmd);
475 : plumed_convert_fptr(finalizep,plumed_symbol_table.functions.finalize);
476 4595 : if(plumed_kernel_register && debug)
477 0 : std::fprintf(stderr,"+++ Registering functions at %p (%p,%p,%p) +++\n",
478 : (void*)&plumed_symbol_table.functions,createp,cmdp,finalizep);
479 4595 : if(plumed_kernel_register) {
480 13 : (*plumed_kernel_register)(&plumed_symbol_table.functions);
481 : }
482 : // Notice that handle could be null in the following cases:
483 : // - if we use RTLD_DEFAULT
484 : // - on Linux if we don't use RTLD_DEFAULT, since dlopen(NULL,RTLD_LOCAL) returns a null pointer.
485 : if(handle) {
486 : dlclose(handle);
487 : }
488 : #endif
489 : }
490 4595 : ~PlumedMainInitializer() {
491 4595 : if(debug) {
492 0 : std::fprintf(stderr,"+++ Finalizing PLUMED with plumed_symbol_table at %p\n",(void*)&plumed_symbol_table);
493 : }
494 4595 : }
495 : } PlumedMainInitializerRegisterMe;
496 : }
497 :
498 : }
499 :
500 :
|