// // ******************************************************************** // * 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. * // ******************************************************************** // // // --------------------------------------------------------------- // GEANT 4 class header file // // 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. // History: // 21 October 2013: A. Dotti - 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: typedef VALTYPE value_type; // 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< std::vector > { public: // Some useful definitions // typedef VALTYPE value_type; typedef typename std::vector vector_type; typedef typename vector_type::size_type size_type; typedef typename vector_type::iterator iterator; typedef typename vector_type::const_iterator 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 // typedef KEYTYPE key_type; typedef VALTYPE value_type; typedef typename std::map map_type; typedef typename map_type::size_type size_type; typedef typename map_type::iterator iterator; typedef typename map_type::const_iterator 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++; #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 G4VERBOSE G4cout << "Non-critical error: mutex lock failure in ~G4Cache<" << typeid(V).name() << ">. " << G4endl << "If the RunManagerKernel has been deleted, it failed to " << "delete an allocated resource" << G4endl << "and this destructor is being called after the statics " << "were destroyed." << G4endl; G4cout << "Exception: [code: " << e.code() << "] caught: " << e.what() << G4endl; #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() { } 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<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