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 : const std::string& getMissingKey() const { 120 2 : return missingKey; 121 : } 122 : template<typename T> 123 : ExceptionRegisterError& operator<<(const T & x) { 124 2 : *static_cast<Exception*>(this) <<x; 125 2 : return *this; 126 : } 127 : }; 128 : 129 : /// General register. 130 : /// This class provide a generic implementation based on the content of the Register 131 : template<class Content> 132 : class RegisterBase : 133 : public Register { 134 : 135 : public: 136 : /// auxiliary class 137 5388841 : struct ContentAndFullPath { 138 : Content content; 139 : std::string fullPath; 140 : }; 141 : 142 : private: 143 : /// Main register map 144 : std::map<std::string,std::unique_ptr<ContentAndFullPath>> m; 145 : /// Map of staged keys 146 : std::map<std::string,std::unique_ptr<ContentAndFullPath>> staged_m; 147 : 148 : public: 149 : 150 : struct ID { 151 : ContentAndFullPath* ptr{nullptr}; 152 : }; 153 : /// Register a new class. 154 : /// \param key The name of the directive to be used in the input file 155 : /// \param content The registered content 156 : /// \param ID A returned ID that can be used to remove the directive later 157 : ID add(std::string key,const Content & content); 158 : 159 : /// Verify if a key is present in the register, accessing to registered images 160 : bool check(const std::vector<void*> & images,const std::string & key) const; 161 : 162 : /// Verify if a key is present in the register, only considering the default image 163 : bool check(const std::string & key) const; 164 : 165 : /// Return the content associated to a key in the register, accessing to registerd images 166 : const Content & get(const std::vector<void*> & images,const std::string & key) const; 167 : 168 : /// Return the full path associated to a key in the register, accessing to registerd images 169 : const std::string & getFullPath(const std::vector<void*> & images,const std::string & key) const; 170 : 171 : /// Return the content associated to a key in the register, only considering the default image 172 : const Content & get(const std::string & key) const; 173 : 174 : /// Remove a registered keyword. 175 : /// Use the ID returned by add(). 176 : void remove(ID id); 177 : 178 : /// Get a list of keys 179 : /// Notice that these are the keys in the map, not the plumed keywords! 180 : /// Also notice that this list includes keys from all images, including the 181 : /// textual version of the image void* 182 : std::vector<std::string> getKeys() const override; 183 : 184 : ~RegisterBase() noexcept override; 185 : 186 : /// complete registration 187 : /// all staged keys will be enabled 188 : /// Should be called after dlopen has been completed correctly. 189 : void completeRegistration(void*handle) override; 190 : 191 : void clearStaged() noexcept override; 192 : 193 : }; 194 : 195 : template<class Content> 196 2694432 : typename RegisterBase<Content>::ID RegisterBase<Content>::add(std::string key,const Content & content) { 197 : 198 5388864 : auto ptr=std::make_unique<ContentAndFullPath>(ContentAndFullPath{content,getRegisteringFullPath()}); 199 : ID id{ptr.get()}; 200 : 201 : // lock map for writing 202 2694432 : std::unique_lock<std::shared_mutex> lock(mutex); 203 : 204 2694432 : if(isDLRegistering()) { 205 0 : plumed_assert(!staged_m.count(key)) << "cannot stage key twice with the same name "<< key<<"\n"; 206 18 : staged_m.insert({key, std::move(ptr)}); 207 : } else { 208 0 : plumed_assert(!m.count(key)) << "cannot register key twice with the same name "<< key<<"\n"; 209 2694414 : m.insert({key, std::move(ptr)}); 210 : } 211 5388864 : return id; 212 2694432 : } 213 : std::ostream & operator<<(std::ostream &log,const Register ®); 214 : 215 : template<class Content> 216 2 : bool RegisterBase<Content>::check(const std::vector<void*> & images,const std::string & key) const { 217 : // lock map for reading 218 2 : std::shared_lock<std::shared_mutex> lock(mutex); 219 : if(m.count(key)>0) { 220 1 : return true; 221 : } 222 1 : for(auto image : images) { 223 0 : std::string k=imageToString(image)+":"+key; 224 : if(m.count(k)>0) { 225 : return true; 226 : } 227 : } 228 : return false; 229 : } 230 : 231 : template<class Content> 232 35759 : bool RegisterBase<Content>::check(const std::string & key) const { 233 : // lock map for reading 234 35759 : std::shared_lock<std::shared_mutex> lock(mutex); 235 35759 : return m.count(key)>0; 236 : } 237 : 238 : template<class Content> 239 55563 : const Content & RegisterBase<Content>::get(const std::vector<void*> & images,const std::string & key) const { 240 : // lock map for reading 241 55563 : std::shared_lock<std::shared_mutex> lock(mutex); 242 55608 : for(auto image = images.rbegin(); image != images.rend(); ++image) { 243 194 : auto qualified_key=imageToString(*image) + ":" + key; 244 : if(m.count(qualified_key)>0) { 245 51 : return m.find(qualified_key)->second->content; 246 : } 247 : } 248 : if (m.count(key) == 0 ) { 249 4 : throw ExceptionRegisterError().setMissingKey(key); 250 : } 251 55510 : return m.find(key)->second->content; 252 : } 253 : 254 : template<class Content> 255 51074 : const std::string & RegisterBase<Content>::getFullPath(const std::vector<void*> & images,const std::string & key) const { 256 : // lock map for reading 257 51074 : std::shared_lock<std::shared_mutex> lock(mutex); 258 51119 : for(auto image = images.rbegin(); image != images.rend(); ++image) { 259 172 : auto qualified_key=imageToString(*image) + ":" + key; 260 : if(m.count(qualified_key)>0) { 261 41 : return m.find(qualified_key)->second->fullPath; 262 : } 263 : } 264 : if (m.count(key) == 0 ) { 265 0 : throw ExceptionRegisterError().setMissingKey(key); 266 : } 267 51033 : return m.find(key)->second->fullPath; 268 : } 269 : 270 : template<class Content> 271 23648 : const Content & RegisterBase<Content>::get(const std::string & key) const { 272 : // lock map for reading 273 23648 : std::shared_lock<std::shared_mutex> lock(mutex); 274 : if (m.count(key) == 0 ) { 275 0 : throw ExceptionRegisterError().setMissingKey(key); 276 : } 277 23648 : return m.find(key)->second->content; 278 : } 279 : 280 : template<class Content> 281 2694414 : void RegisterBase<Content>::remove(ID id) { 282 : // lock map for writing 283 2694414 : std::unique_lock<std::shared_mutex> lock(mutex); 284 2694414 : if(id.ptr) { 285 329265627 : for(auto p=m.begin(); p!=m.end(); ++p) { 286 329265627 : if(p->second.get()==id.ptr) { 287 2694414 : m.erase(p); 288 : break; 289 : } 290 : } 291 : } 292 2694414 : } 293 : 294 : template<class Content> 295 6917 : std::vector<std::string> RegisterBase<Content>::getKeys() const { 296 : // lock map for reading 297 6917 : std::shared_lock<std::shared_mutex> lock(mutex); 298 : std::vector<std::string> s; 299 526501 : for(const auto & it : m) { 300 519584 : s.push_back(it.first); 301 : } 302 6917 : std::sort(s.begin(),s.end()); 303 6917 : return s; 304 0 : } 305 : 306 : template<class Content> 307 11766 : RegisterBase<Content>::~RegisterBase() noexcept { 308 11766 : if(m.size()>0) { 309 0 : std::string names=""; 310 0 : for(const auto & p : m) { 311 0 : names+=p.first+" "; 312 : } 313 0 : std::cerr<<"WARNING: Directive "+ names +" has not been properly unregistered. This might lead to memory leak!!\n"; 314 : } 315 23532 : } 316 : 317 : template<class Content> 318 102 : void RegisterBase<Content>::completeRegistration(void*handle) { 319 : // lock map for writing 320 102 : std::unique_lock<std::shared_mutex> lock(mutex); 321 120 : for (auto iter = staged_m.begin(); iter != staged_m.end(); ) { 322 36 : auto key = imageToString(handle) + ":" + iter->first; 323 0 : plumed_assert(!m.count(key)) << "cannot register key twice with the same name "<< key<<"\n"; 324 18 : m[key] = std::move(iter->second); 325 : // Since we've moved out the value, we can safely erase the element from the original map 326 : // This also avoids invalidating our iterator since erase returns the next iterator 327 18 : iter = staged_m.erase(iter); 328 : } 329 102 : plumed_assert(staged_m.empty()); 330 102 : } 331 : 332 : template<class Content> 333 102 : void RegisterBase<Content>::clearStaged() noexcept { 334 : // lock map for writing 335 102 : std::unique_lock<std::shared_mutex> lock(mutex); 336 : staged_m.clear(); 337 102 : } 338 : } 339 : 340 : #endif