LCOV - code coverage report
Current view: top level - core - PlumedMainInitializer.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 102 143 71.3 %
Date: 2024-10-11 08:09:47 Functions: 9 11 81.8 %

          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             : 

Generated by: LCOV version 1.15