LCOV - code coverage report
Current view: top level - core - RegisterBase.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 62 72 86.1 %
Date: 2024-10-18 14:00:25 Functions: 19 22 86.4 %

          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 &reg);
      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 &reg);
     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

Generated by: LCOV version 1.16