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 : #ifndef __PLUMED_core_RegisterBase_h 23 : #define __PLUMED_core_RegisterBase_h 24 : 25 : #include "tools/Exception.h" 26 : #include <string> 27 : #include <string_view> 28 : #include <map> 29 : #include <memory> 30 : #include <iostream> 31 : #include <vector> 32 : #include <algorithm> 33 : #include <mutex> 34 : #include <shared_mutex> 35 : 36 : namespace PLMD { 37 : 38 : /// Base class, with type independent information. 39 : /// Actual registers should inherit through the RegisterBase class below 40 : class Register { 41 : /// Initialize registration - only used by registrationLock() 42 : static void pushDLRegistration(const std::string & fullpath); 43 : /// Finalize registration - only used by registrationLock() 44 : static void popDLRegistration() noexcept; 45 : 46 : protected: 47 : /// Mutex protecting access to map 48 : mutable std::shared_mutex mutex; 49 : /// Internal tool to format image addresses 50 : static std::string imageToString(void* image); 51 : /// Check if we are in a dlopen section 52 : static bool isDLRegistering() noexcept; 53 : /// Return the path of the currently-loading library 54 : static const std::string getRegisteringFullPath() noexcept; 55 : /// Save all staged objects from a register 56 : virtual void completeRegistration(void* image)=0; 57 : /// Clear staged objects. 58 : /// Should be used when leaving the dlopen section to remove 59 : /// any dangling object. 60 : virtual void clearStaged() noexcept =0; 61 : /// Get all registered keys. 62 : /// These are the keys in the map, not the plumed keywords! 63 : virtual std::vector<std::string> getKeys() const =0; 64 : 65 : public: 66 : /// Constructor. 67 : /// This keeps track of all created instances. 68 : Register(); 69 : /// Destructor. 70 : virtual ~Register() noexcept; 71 : /// Disable move 72 : Register(Register &&) = delete; 73 : /// Disable copy 74 : Register(const Register &) = delete; 75 : 76 : /// Small class to manage registration lock 77 : /// This is used during dlopen, to avoid data races in registrations 78 : class RegistrationLock { 79 : bool active; 80 : public: 81 : RegistrationLock(const std::string & fullpath); 82 : RegistrationLock(const RegistrationLock&) = delete; 83 : RegistrationLock(RegistrationLock&& other) noexcept; 84 : ~RegistrationLock() noexcept; 85 : }; 86 : 87 : /// return a registration lock 88 : static RegistrationLock registrationLock(const std::string & fullpath); 89 : 90 : /// Save all staged objects in all registers 91 : static void completeAllRegistrations(void* image); 92 : 93 : /// Get only keys registered specifically by a given image 94 : std::vector<std::string> getKeysWithDLHandle(void* handle) const; 95 : 96 : friend std::ostream & operator<<(std::ostream &log,const Register ®); 97 : }; 98 : 99 : /// Class representing an error in a register 100 2 : class ExceptionRegisterError : 101 : public Exception { 102 : /// The missing key 103 : std::string missingKey; 104 : public: 105 : using Exception::Exception; 106 : /// Sets the missing key 107 : /// \param key The missing key 108 : /// \return This exception 109 : /// 110 : /// ExceptionRegisterError can be used as a builder pattern: 111 : /// `throw ExceptionRegisterError().setMissingKey(key);` 112 : /// 113 : /// the key can be retrieved with ExceptionRegisterError::getMissingKey() 114 : ExceptionRegisterError& setMissingKey (std::string_view key) { 115 : missingKey=key; 116 : return *this; 117 : } 118 : /// Returns the missing key 119 2 : const std::string& getMissingKey() const {return missingKey;} 120 : template<typename T> 121 : ExceptionRegisterError& operator<<(const T & x) { 122 2 : *static_cast<Exception*>(this) <<x; 123 2 : return *this; 124 : } 125 : }; 126 : 127 : /// General register. 128 : /// This class provide a generic implementation based on the content of the Register 129 : template<class Content> 130 : class RegisterBase : 131 : public Register { 132 : 133 : public: 134 : /// auxiliary class 135 4858837 : struct ContentAndFullPath { 136 : Content content; 137 : std::string fullPath; 138 : }; 139 : 140 : private: 141 : /// Main register map 142 : std::map<std::string,std::unique_ptr<ContentAndFullPath>> m; 143 : /// Map of staged keys 144 : std::map<std::string,std::unique_ptr<ContentAndFullPath>> staged_m; 145 : 146 : public: 147 : 148 : struct ID { 149 : ContentAndFullPath* ptr{nullptr}; 150 : }; 151 : /// Register a new class. 152 : /// \param key The name of the directive to be used in the input file 153 : /// \param content The registered content 154 : /// \param ID A returned ID that can be used to remove the directive later 155 : ID add(std::string key,const Content & content); 156 : 157 : /// Verify if a key is present in the register, accessing to registered images 158 : bool check(const std::vector<void*> & images,const std::string & key) const; 159 : 160 : /// Verify if a key is present in the register, only considering the default image 161 : bool check(const std::string & key) const; 162 : 163 : /// Return the content associated to a key in the register, accessing to registerd images 164 : const Content & get(const std::vector<void*> & images,const std::string & key) const; 165 : 166 : /// Return the full path associated to a key in the register, accessing to registerd images 167 : const std::string & getFullPath(const std::vector<void*> & images,const std::string & key) const; 168 : 169 : /// Return the content associated to a key in the register, only considering the default image 170 : const Content & get(const std::string & key) const; 171 : 172 : /// Remove a registered keyword. 173 : /// Use the ID returned by add(). 174 : void remove(ID id); 175 : 176 : /// Get a list of keys 177 : /// Notice that these are the keys in the map, not the plumed keywords! 178 : /// Also notice that this list includes keys from all images, including the 179 : /// textual version of the image void* 180 : std::vector<std::string> getKeys() const override; 181 : 182 : ~RegisterBase() noexcept override; 183 : 184 : /// complete registration 185 : /// all staged keys will be enabled 186 : /// Should be called after dlopen has been completed correctly. 187 : void completeRegistration(void*handle) override; 188 : 189 : void clearStaged() noexcept override; 190 : 191 : }; 192 : 193 : template<class Content> 194 2429433 : typename RegisterBase<Content>::ID RegisterBase<Content>::add(std::string key,const Content & content) { 195 : 196 4858866 : auto ptr=std::make_unique<ContentAndFullPath>(ContentAndFullPath{content,getRegisteringFullPath()}); 197 : ID id{ptr.get()}; 198 : 199 : // lock map for writing 200 2429433 : std::unique_lock<std::shared_mutex> lock(mutex); 201 : 202 2429433 : if(isDLRegistering()) { 203 0 : plumed_assert(!staged_m.count(key)) << "cannot stage key twice with the same name "<< key<<"\n"; 204 21 : staged_m.insert({key, std::move(ptr)}); 205 : } else { 206 0 : plumed_assert(!m.count(key)) << "cannot register key twice with the same name "<< key<<"\n"; 207 2429412 : m.insert({key, std::move(ptr)}); 208 : } 209 4858866 : return id; 210 2429433 : } 211 : std::ostream & operator<<(std::ostream &log,const Register ®); 212 : 213 : template<class Content> 214 2 : bool RegisterBase<Content>::check(const std::vector<void*> & images,const std::string & key) const { 215 : // lock map for reading 216 2 : std::shared_lock<std::shared_mutex> lock(mutex); 217 1 : if(m.count(key)>0) return true; 218 1 : for(auto image : images) { 219 0 : std::string k=imageToString(image)+":"+key; 220 : if(m.count(k)>0) return true; 221 : } 222 : return false; 223 : } 224 : 225 : template<class Content> 226 34613 : bool RegisterBase<Content>::check(const std::string & key) const { 227 : // lock map for reading 228 34613 : std::shared_lock<std::shared_mutex> lock(mutex); 229 34613 : return m.count(key)>0; 230 : } 231 : 232 : template<class Content> 233 55162 : const Content & RegisterBase<Content>::get(const std::vector<void*> & images,const std::string & key) const { 234 : // lock map for reading 235 55162 : std::shared_lock<std::shared_mutex> lock(mutex); 236 55207 : for(auto image = images.rbegin(); image != images.rend(); ++image) { 237 194 : auto qualified_key=imageToString(*image) + ":" + key; 238 51 : if(m.count(qualified_key)>0) return m.find(qualified_key)->second->content; 239 : } 240 : if (m.count(key) == 0 ) { 241 4 : throw ExceptionRegisterError().setMissingKey(key); 242 : } 243 55109 : return m.find(key)->second->content; 244 : } 245 : 246 : template<class Content> 247 51204 : const std::string & RegisterBase<Content>::getFullPath(const std::vector<void*> & images,const std::string & key) const { 248 : // lock map for reading 249 51204 : std::shared_lock<std::shared_mutex> lock(mutex); 250 51249 : for(auto image = images.rbegin(); image != images.rend(); ++image) { 251 172 : auto qualified_key=imageToString(*image) + ":" + key; 252 41 : if(m.count(qualified_key)>0) return m.find(qualified_key)->second->fullPath; 253 : } 254 : if (m.count(key) == 0 ) { 255 0 : throw ExceptionRegisterError().setMissingKey(key); 256 : } 257 51163 : return m.find(key)->second->fullPath; 258 : } 259 : 260 : template<class Content> 261 22979 : const Content & RegisterBase<Content>::get(const std::string & key) const { 262 : // lock map for reading 263 22979 : std::shared_lock<std::shared_mutex> lock(mutex); 264 : if (m.count(key) == 0 ) { 265 0 : throw ExceptionRegisterError().setMissingKey(key); 266 : } 267 22979 : return m.find(key)->second->content; 268 : } 269 : 270 : template<class Content> 271 2429412 : void RegisterBase<Content>::remove(ID id) { 272 : // lock map for writing 273 2429412 : std::unique_lock<std::shared_mutex> lock(mutex); 274 2429412 : if(id.ptr) { 275 296521164 : for(auto p=m.begin(); p!=m.end(); ++p) { 276 296521164 : if(p->second.get()==id.ptr) { 277 2429412 : m.erase(p); break; 278 : } 279 : } 280 : } 281 2429412 : } 282 : 283 : template<class Content> 284 5490 : std::vector<std::string> RegisterBase<Content>::getKeys() const { 285 : // lock map for reading 286 5490 : std::shared_lock<std::shared_mutex> lock(mutex); 287 : std::vector<std::string> s; 288 311847 : for(const auto & it : m) s.push_back(it.first); 289 5490 : std::sort(s.begin(),s.end()); 290 5490 : return s; 291 0 : } 292 : 293 : template<class Content> 294 10632 : RegisterBase<Content>::~RegisterBase() noexcept { 295 10632 : if(m.size()>0) { 296 0 : std::string names=""; 297 0 : for(const auto & p : m) names+=p.first+" "; 298 0 : std::cerr<<"WARNING: Directive "+ names +" has not been properly unregistered. This might lead to memory leak!!\n"; 299 : } 300 21264 : } 301 : 302 : template<class Content> 303 102 : void RegisterBase<Content>::completeRegistration(void*handle) { 304 : // lock map for writing 305 102 : std::unique_lock<std::shared_mutex> lock(mutex); 306 123 : for (auto iter = staged_m.begin(); iter != staged_m.end(); ) { 307 42 : auto key = imageToString(handle) + ":" + iter->first; 308 0 : plumed_assert(!m.count(key)) << "cannot register key twice with the same name "<< key<<"\n"; 309 21 : m[key] = std::move(iter->second); 310 : // Since we've moved out the value, we can safely erase the element from the original map 311 : // This also avoids invalidating our iterator since erase returns the next iterator 312 21 : iter = staged_m.erase(iter); 313 : } 314 102 : plumed_assert(staged_m.empty()); 315 102 : } 316 : 317 : template<class Content> 318 102 : void RegisterBase<Content>::clearStaged() noexcept { 319 : // lock map for writing 320 102 : std::unique_lock<std::shared_mutex> lock(mutex); 321 : staged_m.clear(); 322 102 : } 323 : } 324 : 325 : #endif