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 :
45 :
46 12501 : static bool getenvTypesafeDebug() noexcept {
47 12501 : static const auto* res=std::getenv("PLUMED_TYPESAFE_DEBUG");
48 12501 : return res;
49 : }
50 :
51 0 : static void typesafeDebug(const char*key,plumed_safeptr_x safe) noexcept {
52 0 : std::fprintf(stderr,"+++ PLUMED_TYPESAFE_DEBUG %s %p %zu",key,safe.ptr,safe.nelem);
53 0 : const size_t* shape=safe.shape;
54 0 : if(shape) {
55 0 : std::fprintf(stderr," (");
56 0 : while(*shape!=0) {
57 0 : std::fprintf(stderr," %zu",*shape);
58 0 : shape++;
59 : }
60 0 : std::fprintf(stderr," )");
61 : }
62 0 : std::fprintf(stderr," %zx %p\n",safe.flags,safe.opt);
63 0 : }
64 :
65 : // create should never throw
66 : // in case of a problem, it logs the error and return a null pointer
67 : // when loaded by an interface >=2.5, this will result in a non valid plumed object.
68 : // earlier interfaces will just give a segfault or a failed assertion.
69 403572 : extern "C" void*plumed_plumedmain_create() {
70 : try {
71 403572 : return new PLMD::PlumedMain;
72 0 : } catch(const std::exception & e) {
73 0 : std::cerr<<"+++ an error happened while creating a plumed object\n";
74 0 : std::cerr<<e.what()<<std::endl;
75 : return nullptr;
76 0 : } catch(...) {
77 0 : std::cerr<<"+++ an unknown error happened while creating a plumed object"<<std::endl;
78 : return nullptr;
79 0 : }
80 : }
81 :
82 357 : extern "C" void plumed_plumedmain_cmd(void*plumed,const char*key,const void*val) {
83 357 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
84 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
85 714 : p->cmd(key,PLMD::TypesafePtr::unchecked(val));
86 357 : }
87 :
88 : extern "C" {
89 93 : static void plumed_plumedmain_cmd_safe(void*plumed,const char*key,plumed_safeptr_x safe) {
90 93 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
91 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
92 93 : if(getenvTypesafeDebug()) typesafeDebug(key,safe);
93 186 : p->cmd(key,PLMD::TypesafePtr::fromSafePtr(&safe));
94 93 : }
95 : }
96 :
97 : extern "C" {
98 12408 : static void plumed_plumedmain_cmd_safe_nothrow(void*plumed,const char*key,plumed_safeptr_x safe,plumed_nothrow_handler_x nothrow) {
99 : // This is a workaround for a suboptimal choice in PLUMED <2.8
100 : // In particular, the only way to bypass the exception handling process was to call the plumed_plumedmain_cmd_safe
101 : // function directly.
102 : // With this modification, it is possible to just call the plumed_plumedmain_cmd_safe_nothrow function
103 : // passing a null error handler.
104 12408 : if(!nothrow.handler) {
105 0 : plumed_plumedmain_cmd_safe(plumed,key,safe);
106 0 : return;
107 : }
108 : // At library boundaries we translate exceptions to error codes.
109 : // This allows an exception to be catched also if the MD code
110 : // was linked against a different C++ library
111 : try {
112 12408 : plumed_massert(plumed,"trying to use a plumed object which is not initialized");
113 : auto p=static_cast<PLMD::PlumedMain*>(plumed);
114 12408 : if(getenvTypesafeDebug()) typesafeDebug(key,safe);
115 24816 : p->cmd(key,PLMD::TypesafePtr::fromSafePtr(&safe));
116 86 : } catch(const PLMD::ExceptionTypeError & e) {
117 8 : nothrow.handler(nothrow.ptr,20300,e.what(),nullptr);
118 62 : } catch(const PLMD::ExceptionError & e) {
119 54 : nothrow.handler(nothrow.ptr,20200,e.what(),nullptr);
120 55 : } catch(const PLMD::ExceptionDebug & e) {
121 1 : nothrow.handler(nothrow.ptr,20100,e.what(),nullptr);
122 2 : } catch(const PLMD::Exception & e) {
123 1 : nothrow.handler(nothrow.ptr,20000,e.what(),nullptr);
124 2 : } catch(const PLMD::lepton::Exception & e) {
125 1 : nothrow.handler(nothrow.ptr,19900,e.what(),nullptr);
126 : // 11000 to 12000 are "bad exceptions". message will be copied without new allocations
127 2 : } catch(const std::bad_exception & e) {
128 1 : nothrow.handler(nothrow.ptr,11500,e.what(),nullptr);
129 : #ifdef __PLUMED_LIBCXX11
130 2 : } catch(const std::bad_array_new_length & e) {
131 1 : nothrow.handler(nothrow.ptr,11410,e.what(),nullptr);
132 : #endif
133 2 : } catch(const std::bad_alloc & e) {
134 1 : nothrow.handler(nothrow.ptr,11400,e.what(),nullptr);
135 : #ifdef __PLUMED_LIBCXX11
136 2 : } catch(const std::bad_function_call & e) {
137 1 : nothrow.handler(nothrow.ptr,11300,e.what(),nullptr);
138 2 : } catch(const std::bad_weak_ptr & e) {
139 1 : nothrow.handler(nothrow.ptr,11200,e.what(),nullptr);
140 : #endif
141 2 : } catch(const std::bad_cast & e) {
142 1 : nothrow.handler(nothrow.ptr,11100,e.what(),nullptr);
143 2 : } catch(const std::bad_typeid & e) {
144 1 : nothrow.handler(nothrow.ptr,11000,e.what(),nullptr);
145 : // not implemented yet: std::regex_error
146 : // we do not allow regex yet due to portability problems with gcc 4.8
147 : // as soon as we transition to using <regex> it should be straightforward to add
148 2 : } catch(const std::ios_base::failure & e) {
149 : #ifdef __PLUMED_LIBCXX11
150 1 : int value=e.code().value();
151 1 : const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part.
152 1 : if(e.code().category()==std::generic_category()) nothrow.handler(nothrow.ptr,10230,e.what(),opt);
153 1 : else if(e.code().category()==std::system_category()) nothrow.handler(nothrow.ptr,10231,e.what(),opt);
154 1 : else if(e.code().category()==std::iostream_category()) nothrow.handler(nothrow.ptr,10232,e.what(),opt);
155 0 : else if(e.code().category()==std::future_category()) nothrow.handler(nothrow.ptr,10233,e.what(),opt);
156 : else
157 : #endif
158 : // 10239 represents std::ios_base::failure with default constructur
159 0 : nothrow.handler(nothrow.ptr,10239,e.what(),nullptr);
160 : #ifdef __PLUMED_LIBCXX11
161 5 : } catch(const std::system_error & e) {
162 4 : int value=e.code().value();
163 4 : const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part.
164 4 : if(e.code().category()==std::generic_category()) nothrow.handler(nothrow.ptr,10220,e.what(),opt);
165 3 : else if(e.code().category()==std::system_category()) nothrow.handler(nothrow.ptr,10221,e.what(),opt);
166 2 : else if(e.code().category()==std::iostream_category()) nothrow.handler(nothrow.ptr,10222,e.what(),opt);
167 1 : else if(e.code().category()==std::future_category()) nothrow.handler(nothrow.ptr,10223,e.what(),opt);
168 : // fallback to generic runtime_error
169 0 : else nothrow.handler(nothrow.ptr,10200,e.what(),nullptr);
170 : #endif
171 5 : } catch(const std::underflow_error &e) {
172 1 : nothrow.handler(nothrow.ptr,10215,e.what(),nullptr);
173 2 : } catch(const std::overflow_error &e) {
174 1 : nothrow.handler(nothrow.ptr,10210,e.what(),nullptr);
175 2 : } catch(const std::range_error &e) {
176 1 : nothrow.handler(nothrow.ptr,10205,e.what(),nullptr);
177 2 : } catch(const std::runtime_error & e) {
178 1 : nothrow.handler(nothrow.ptr,10200,e.what(),nullptr);
179 : // not implemented yet: std::future_error
180 : // not clear how useful it would be.
181 2 : } catch(const std::out_of_range & e) {
182 1 : nothrow.handler(nothrow.ptr,10120,e.what(),nullptr);
183 2 : } catch(const std::length_error & e) {
184 1 : nothrow.handler(nothrow.ptr,10115,e.what(),nullptr);
185 2 : } catch(const std::domain_error & e) {
186 1 : nothrow.handler(nothrow.ptr,10110,e.what(),nullptr);
187 2 : } catch(const std::invalid_argument & e) {
188 1 : nothrow.handler(nothrow.ptr,10105,e.what(),nullptr);
189 2 : } catch(const std::logic_error & e) {
190 1 : nothrow.handler(nothrow.ptr,10100,e.what(),nullptr);
191 : // generic exception. message will be copied without new allocations
192 : // reports all non caught exceptions that are derived from std::exception
193 : // for instance, boost exceptions would end up here
194 1 : } catch(const std::exception & e) {
195 0 : nothrow.handler(nothrow.ptr,10000,e.what(),nullptr);
196 0 : } catch(...) {
197 : // if exception cannot be translated, we throw a bad_exception
198 0 : nothrow.handler(nothrow.ptr,11500,"plumed could not translate exception",nullptr);
199 0 : throw;
200 0 : }
201 : }
202 : }
203 :
204 : extern "C" {
205 0 : static void plumed_plumedmain_cmd_nothrow(void*plumed,const char*key,const void*val,plumed_nothrow_handler_x nothrow) {
206 : plumed_safeptr_x safe;
207 0 : plumed_assert(nothrow.handler) << "Accepting a null pointer here would make the calling code non compatible with plumed 2.5 to 2.7";
208 0 : safe.ptr=val;
209 0 : safe.nelem=0;
210 0 : safe.shape=NULL;
211 0 : safe.flags=0;
212 0 : safe.opt=NULL;
213 0 : plumed_plumedmain_cmd_safe_nothrow(plumed,key,safe,nothrow);
214 0 : }
215 : }
216 :
217 403572 : extern "C" void plumed_plumedmain_finalize(void*plumed) {
218 403572 : plumed_massert(plumed,"trying to deallocate a plumed object which is not initialized");
219 : // I think it is not possible to replace this delete with a smart pointer
220 : // since the ownership of this pointer is in a C structure. GB
221 403572 : delete static_cast<PLMD::PlumedMain*>(plumed);
222 403572 : }
223 :
224 : // values here should be consistent with those in plumed_symbol_table_init !!!!
225 : plumed_symbol_table_type_x plumed_symbol_table= {
226 : 3,
227 : {plumed_plumedmain_create,plumed_plumedmain_cmd,plumed_plumedmain_finalize},
228 : plumed_plumedmain_cmd_nothrow,
229 : plumed_plumedmain_cmd_safe,
230 : plumed_plumedmain_cmd_safe_nothrow
231 : };
232 :
233 : // values here should be consistent with those above !!!!
234 407021 : extern "C" void plumed_symbol_table_init() {
235 407021 : plumed_symbol_table.version=3;
236 407021 : plumed_symbol_table.functions.create=plumed_plumedmain_create;
237 407021 : plumed_symbol_table.functions.cmd=plumed_plumedmain_cmd;
238 407021 : plumed_symbol_table.functions.finalize=plumed_plumedmain_finalize;
239 407021 : plumed_symbol_table.cmd_nothrow=plumed_plumedmain_cmd_nothrow;
240 407021 : plumed_symbol_table.cmd_safe=plumed_plumedmain_cmd_safe;
241 407021 : plumed_symbol_table.cmd_safe_nothrow=plumed_plumedmain_cmd_safe_nothrow;
242 407021 : }
243 :
244 : namespace PLMD {
245 :
246 : #define plumed_convert_fptr(ptr,fptr) { ptr=NULL; std::memcpy(&ptr,&fptr,(sizeof(fptr)>sizeof(ptr)?sizeof(ptr):sizeof(fptr))); }
247 :
248 : /// Static object which registers Plumed.
249 : /// This is a static object which, during its construction at startup,
250 : /// registers the pointers to plumed_plumedmain_create, plumed_plumedmain_cmd and plumed_plumedmain_finalize
251 : /// to the plumed_kernel_register function.
252 : /// Registration is only required with plumed loader <=2.4, but we do it anyway in order to maintain
253 : /// backward compatibility. Notice that as of plumed 2.5 the plumed_kernel_register is found
254 : /// using dlsym, in order to allow the libplumedKernel library to be loadable also when
255 : /// the plumed_kernel_register symbol is not available.
256 : namespace {
257 : class PlumedMainInitializer {
258 : const bool debug;
259 : public:
260 3473 : PlumedMainInitializer():
261 3473 : debug(std::getenv("PLUMED_LOAD_DEBUG"))
262 : {
263 : // make sure static plumed_function_pointers is initialized here
264 3473 : plumed_symbol_table_init();
265 3473 : if(debug) std::fprintf(stderr,"+++ Initializing PLUMED with plumed_symbol_table version %i at %p\n",plumed_symbol_table.version,(void*)&plumed_symbol_table);
266 : #if defined(__PLUMED_HAS_DLOPEN)
267 3473 : if(std::getenv("PLUMED_LOAD_SKIP_REGISTRATION")) {
268 0 : if(debug) std::fprintf(stderr,"+++ Skipping registration +++\n");
269 0 : return;
270 : }
271 : typedef plumed_plumedmain_function_holder_x* (*plumed_kernel_register_type_x)(const plumed_plumedmain_function_holder_x*);
272 : plumed_kernel_register_type_x plumed_kernel_register=nullptr;
273 : void* handle=nullptr;
274 : #if defined(__PLUMED_HAS_RTLD_DEFAULT)
275 3473 : if(debug) std::fprintf(stderr,"+++ Registering functions. Looking in RTLD_DEFAULT +++\n");
276 3473 : void* dls=dlsym(RTLD_DEFAULT,"plumed_kernel_register");
277 : #else
278 : handle=dlopen(NULL,RTLD_LOCAL);
279 : if(debug) std::fprintf(stderr,"+++ Registering functions. dlopen handle at %p +++\n",handle);
280 : void* dls=dlsym(handle,"plumed_kernel_register");
281 : #endif
282 3473 : *(void **)(&plumed_kernel_register)=dls;
283 3473 : if(debug) {
284 0 : if(plumed_kernel_register) {
285 0 : std::fprintf(stderr,"+++ plumed_kernel_register found at %p +++\n",dls);
286 : }
287 0 : else std::fprintf(stderr,"+++ plumed_kernel_register not found +++\n");
288 : }
289 : void*createp;
290 : void*cmdp;
291 : void*finalizep;
292 : plumed_convert_fptr(createp,plumed_symbol_table.functions.create);
293 : plumed_convert_fptr(cmdp,plumed_symbol_table.functions.cmd);
294 : plumed_convert_fptr(finalizep,plumed_symbol_table.functions.finalize);
295 3473 : if(plumed_kernel_register && debug) std::fprintf(stderr,"+++ Registering functions at %p (%p,%p,%p) +++\n",
296 : (void*)&plumed_symbol_table.functions,createp,cmdp,finalizep);
297 3473 : if(plumed_kernel_register) (*plumed_kernel_register)(&plumed_symbol_table.functions);
298 : // Notice that handle could be null in the following cases:
299 : // - if we use RTLD_DEFAULT
300 : // - on Linux if we don't use RTLD_DEFAULT, since dlopen(NULL,RTLD_LOCAL) returns a null pointer.
301 : if(handle) dlclose(handle);
302 : #endif
303 : }
304 3473 : ~PlumedMainInitializer() {
305 3473 : if(debug) std::fprintf(stderr,"+++ Finalizing PLUMED with plumed_symbol_table at %p\n",(void*)&plumed_symbol_table);
306 3473 : }
307 : } PlumedMainInitializerRegisterMe;
308 : }
309 :
310 : }
311 :
312 :
|