1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2018-2021 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 "PlumedHandle.h"
23 #include "core/PlumedMain.h"
24 #include "Tools.h"
25 #include <cstring>
26 #ifdef __PLUMED_HAS_DLOPEN
27 #include <dlfcn.h>
28 #endif
29
30 namespace PLMD
31 {
32
~DlHandle()33 PlumedHandle::DlHandle::~DlHandle() {
34 #ifdef __PLUMED_HAS_DLOPEN
35 if(handle) dlclose(handle);
36 #endif
37 }
38
PlumedHandle()39 PlumedHandle::PlumedHandle():
40 local(new PlumedMain)
41 {
42 }
43
PlumedHandle(const char * kernel)44 PlumedHandle::PlumedHandle(const char* kernel)
45 #ifdef __PLUMED_HAS_DLOPEN
46 :
47 handle([&]() {
48 dlerror();
49 int mode = RTLD_LOCAL | RTLD_NOW;
50 #ifdef RTLD_DEEPBIND
51 // Needed on Linux to avoid namespace clashes
52 mode |= RTLD_DEEPBIND;
53 #endif
54 void* h=::dlopen(kernel,mode);
55 // try to remove the "Kernel" string.
56 // needed to load old versions
57 if(!h) {
58 std::string k(kernel);
59 auto i=k.rfind("Kernel");
60 if(i!=std::string::npos) {
61 k=k.substr(0,i) + k.substr(i+6);
62 h=::dlopen(k.c_str(),mode);
63 }
64 }
65 plumed_assert(h) << "there was a problem loading kernel "<<kernel <<"\n"<<dlerror();
66 return DlHandle(h);
67 // once the DlHandle has been constructed we know that later exceptions will also call dlclose().
68 }()),
69 symbol_((plumed_symbol_table_type*) dlsym(handle,"plumed_symbol_table")),
__anonfd38ce500202() 70 create_([&]() {
71 if(symbol_) {
72 plumed_assert(symbol_->functions.create);
73 return symbol_->functions.create;
74 }
75 void* c=nullptr;
76 if(!c) c=dlsym(handle,"plumedmain_create");
77 if(!c) c=dlsym(handle,"plumed_plumedmain_create");
78 plumed_assert(c) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_create";
79 plumed_create_pointer cc;
80 *(void **)(&cc)=c;
81 return cc;
82 }()),
__anonfd38ce500302() 83 cmd_([&]() {
84 if(symbol_) {
85 plumed_assert(symbol_->functions.cmd);
86 return symbol_->functions.cmd;
87 }
88 void* c=nullptr;
89 if(!c) c=dlsym(handle,"plumedmain_cmd");
90 if(!c) c=dlsym(handle,"plumed_plumedmain_cmd");
91 plumed_assert(c) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_cmd";
92 plumed_cmd_pointer cc;
93 *(void **)(&cc)=c;
94 return cc;
95 }()),
__anonfd38ce500402() 96 finalize_([&]() {
97 if(symbol_) {
98 plumed_assert(symbol_->functions.finalize);
99 return symbol_->functions.finalize;
100 }
101 void* f=nullptr;
102 if(!f) f=dlsym(handle,"plumedmain_finalize");
103 if(!f) f=dlsym(handle,"plumed_plumedmain_finalize");
104 plumed_assert(f) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_finalize";
105 plumed_finalize_pointer ff;
106 *(void **)(&ff)=f;
107 return ff;
108 }()),
109 p(create_())
110 // No exceptions thrown past this point.
111 // Thus, destructor PlumedHandle::~PlumedHandle() will always be called and p will always be finalized.
112 {}
113 #else
114 {
115 plumed_error() << "You are trying to dynamically load a kernel, but PLUMED was compiled without dlopen";
116 }
117 #endif
118
~PlumedHandle()119 PlumedHandle::~PlumedHandle() {
120 #ifdef __PLUMED_HAS_DLOPEN
121 if(p) finalize_(p);
122 #endif
123 }
124
dlopen(const char * path)125 PlumedHandle PlumedHandle::dlopen(const char* path) {
126 return PlumedHandle(path);
127 }
128
cmd(const char * key,const void * ptr)129 void PlumedHandle::cmd(const char*key,const void*ptr) {
130 if(local) local->cmd(key,ptr);
131 else if(p && cmd_) cmd_(p,key,ptr);
132 else plumed_error() << "should never arrive here (either one or the other should work)";
133 }
134
135 }
136