// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // G4Cache // // Class Description: // // Helper classes for Geant4 Multi-Threaded. // The classes defined in this header file provide a thread-private // cache to store a thread-local variable V in a class instance // shared among threads. // These are templated classes on the to-be-stored object. // // Example: // Let's assume an instance myObject of class G4Shared is sharead between // threads. Still a data member of this class needs to be thread-private. // A typical example of this being a "cache" for a local calculation. // The helper defined here can be used to guarantee thread-safe operations // on the thread-private object. // Example: // class G4Shared // { // G4double sharedData; // G4Cache threadPrivate; // void foo() // { // G4double priv = threadPrivate.Get(); // if ( priv < 10 ) priv += sharedData; // threadPrivate.Put( priv ); // } // } // // Two variants of the base G4Cache exist. The first one being // G4VectorCache similar to std::vector. // Example: // G4VectorCache aVect; // aVect.Push_back( 3.2 ); // aVect.Push_back( 4.1 ); // std::cout << aVect[0] << std::endl; // The second one being: // G4MapCache, similar to std::map. // Example: // G4MapCache aMap; // aMap[320]=1.234; // // See classes definition for details. // Author: A.Dotti, 21 October 2013 - First implementation // -------------------------------------------------------------------- #ifndef G4CACHE_HH #define G4CACHE_HH // Debug this code // #define g4cdebug 1 #include #include #include #include "G4AutoLock.hh" #include "G4CacheDetails.hh" // Thread Local storage details are here // A templated cache to store a thread-private data of type VALTYPE. // template class G4Cache { public: using value_type = VALTYPE; // The stored type G4Cache(); // Default constructor G4Cache(const value_type& v); // Construct cache object with initial value virtual ~G4Cache(); // Default destructor inline value_type& Get() const; // Gets reference to cached value of this threads inline void Put(const value_type& val) const; // Sets this thread cached value to val inline value_type Pop(); // Gets copy of cached value G4Cache(const G4Cache& rhs); G4Cache& operator=(const G4Cache& rhs); protected: const G4int& GetId() const { return id; } private: G4int id; mutable G4CacheReference theCache; static std::atomic instancesctr; static std::atomic dstrctr; inline value_type& GetCache() const { theCache.Initialize(id); return theCache.GetCache(id); } }; // A vector version of the cache. Implements vector interface. // Can be used directly as a std::vector would be used. // template class G4VectorCache : public G4Cache> { public: // Some useful definitions // using value_type = VALTYPE; using vector_type = typename std::vector; using size_type = typename vector_type::size_type; using iterator = typename vector_type::iterator; using const_iterator = typename vector_type::const_iterator; G4VectorCache(); // Default constructor G4VectorCache(G4int nElems); // Creates a vector cache of nElems elements G4VectorCache(G4int nElems, value_type* vals); // Creates a vector cache with elements from an array virtual ~G4VectorCache(); // Default destructor // Interface with functionalities of similar name of std::vector // inline void Push_back(const value_type& val); inline value_type Pop_back(); inline value_type& operator[](const G4int& idx); inline iterator Begin(); inline iterator End(); inline void Clear(); inline size_type Size() { return G4Cache::Get().size(); } // Needs to be here for a VC9 compilation problem }; // a Map version of the cache. Implements std::map interface. // Can be used directly as a std::map would be used. // KEYTYPE being the key type and VALTYPE the value type. // template class G4MapCache : public G4Cache> { public: // Some useful definitions // using key_type = KEYTYPE; using value_type = VALTYPE; using map_type = typename std::map; using size_type = typename map_type::size_type; using iterator = typename map_type::iterator; using const_iterator = typename map_type::const_iterator; virtual ~G4MapCache(); // Default destructor inline G4bool Has(const key_type& k); // Returns true if map contains element corresponding to key k // Interface with functionalities of similar name of std::map // inline std::pair Insert(const key_type& k, const value_type& v); inline iterator Begin(); inline iterator End(); inline iterator Find(const key_type& k); inline value_type& Get(const key_type& k); inline size_type Erase(const key_type& k); inline value_type& operator[](const key_type& k); inline size_type Size() { return G4Cache::Get().size(); } // Needs to be here for a VC9 compilation problem }; //========= Implementation: G4Cache ==================================== template G4Cache::G4Cache() { G4AutoLock l(G4TypeMutex>()); id = instancesctr++; theCache.Initialize(id); #ifdef g4cdebug std::cout << "G4Cache id: " << id << std::endl; #endif } template G4Cache::G4Cache(const G4Cache& rhs) { // Copy is special, we need to copy the content // of the cache, not the cache object if(this == &rhs) return; G4AutoLock l(G4TypeMutex>()); id = instancesctr++; // Force copy of cached data // V aCopy = rhs.GetCache(); Put(aCopy); #ifdef g4cdebug std::cout << "Copy constructor with id: " << id << std::endl; #endif } template G4Cache& G4Cache::operator=(const G4Cache& rhs) { if(this == &rhs) return *this; // Force copy of cached data // V aCopy = rhs.GetCache(); Put(aCopy); #ifdef g4cdebug std::cout << "Assignement operator with id: " << id << std::endl; #endif return *this; } template G4Cache::G4Cache(const V& v) { G4AutoLock l(G4TypeMutex>()); id = instancesctr++; Put(v); #ifdef g4cdebug std::cout << "G4Cache id: " << id << std::endl; #endif } template G4Cache::~G4Cache() { #ifdef g4cdebug std::cout << "~G4Cache id: " << id << std::endl; #endif // don't automatically lock --> wait until we can catch an error // without scoping the G4AutoLock // G4AutoLock l(G4TypeMutex>(), std::defer_lock); // sometimes the mutex is unavailable in destructors so // try to lock the associated mutex, but catch if it fails try { // a system_error in lock means that the mutex is unavailable // we want to throw the error that comes from locking an unavailable // mutex so that we know there is a memory leak // if the mutex is valid, this will hold until the other thread finishes // l.lock(); } catch(std::system_error& e) { // the error that comes from locking an unavailable mutex #ifdef g4cdebug std::cout << "Non-critical error: mutex lock failure in ~G4Cache<" << typeid(V).name() << ">. " << std::endl << "If the RunManagerKernel has been deleted, it failed to " << "delete an allocated resource" << std::endl << "and this destructor is being called after the statics " << "were destroyed." << std::endl; std::cout << "Exception: [code: " << e.code() << "] caught: " << e.what() << std::endl; #endif } ++dstrctr; G4bool last = (dstrctr == instancesctr); theCache.Destroy(id, last); if(last) { instancesctr.store(0); dstrctr.store(0); } } template V& G4Cache::Get() const { return GetCache(); } template void G4Cache::Put(const V& val) const { GetCache() = val; } // Should here remove from cache element? template V G4Cache::Pop() { return GetCache(); } template std::atomic G4Cache::instancesctr(0); template std::atomic G4Cache::dstrctr(0); //========== Implementation: G4VectorCache =========================== template G4VectorCache::G4VectorCache() = default; template G4VectorCache::~G4VectorCache() { #ifdef g4cdebug std::cout << "~G4VectorCache " << G4Cache::vector_type>::GetId() << " with size: " << Size() << "->"; for(size_type i = 0; i < Size(); ++i) std::cout << operator[](i) << ","; std::cout << "<-" << std::endl; #endif } template G4VectorCache::G4VectorCache(G4int nElems) { vector_type& cc = G4Cache::Get(); cc.resize(nElems); } template G4VectorCache::G4VectorCache(G4int nElems, V* vals) { vector_type& cc = G4Cache::Get(); cc.resize(nElems); for(G4int idx = 0; idx < nElems; ++idx) cc[idx] = vals[idx]; } template void G4VectorCache::Push_back(const V& val) { G4Cache::Get().push_back(val); } template V G4VectorCache::Pop_back() { vector_type& cc = G4Cache::Get(); value_type val = cc[cc.size() - 1]; cc.pop_back(); return val; } template V& G4VectorCache::operator[](const G4int& idx) { vector_type& cc = G4Cache::Get(); return cc[idx]; } template typename G4VectorCache::iterator G4VectorCache::Begin() { return G4Cache::Get().begin(); } template typename G4VectorCache::iterator G4VectorCache::End() { return G4Cache::Get().end(); } template void G4VectorCache::Clear() { G4Cache::Get().clear(); } // template // typename G4VectorCache::size_type G4VectorCache::Size() //{ // return G4Cache::Get().size(); //} //======== Implementation: G4MapType =========================== template G4MapCache::~G4MapCache() { #ifdef g4cdebug std::cout << "~G4MacCache " << G4Cache::GetId() << " with size: " << Size() << "->"; for(iterator it = Begin(); it != End(); ++it) std::cout << it->first << ":" << it->second << ","; std::cout << "<-" << std::endl; #endif } template std::pair::iterator, G4bool> G4MapCache::Insert( const K& k, const V& v) { return G4Cache::Get().insert(std::pair(k, v)); } // template // typename G4MapCache::size_type G4MapCache::Size() //{ // return G4Cache::Get().size(); //} template typename G4MapCache::iterator G4MapCache::Begin() { return G4Cache::Get().begin(); } template typename G4MapCache::iterator G4MapCache::End() { return G4Cache::Get().end(); } template typename G4MapCache::iterator G4MapCache::Find(const K& k) { return G4Cache::Get().find(k); } template G4bool G4MapCache::Has(const K& k) { return (Find(k) != End()); } template V& G4MapCache::Get(const K& k) { return Find(k)->second; } template typename G4MapCache::size_type G4MapCache::Erase(const K& k) { return G4Cache::Get().erase(k); } template V& G4MapCache::operator[](const K& k) { return (G4Cache::Get())[k]; } #endif