diff --git a/core/meta/inc/TIsAProxy.h b/core/meta/inc/TIsAProxy.h index d89ff67a84f08..a4a1e6105de48 100644 --- a/core/meta/inc/TIsAProxy.h +++ b/core/meta/inc/TIsAProxy.h @@ -18,7 +18,7 @@ #ifndef ROOT_Rtypes #include "Rtypes.h" #endif - +#include class TClass; @@ -28,20 +28,22 @@ class TClass; // // ////////////////////////////////////////////////////////////////////////// class TIsAProxy : public TVirtualIsAProxy { - private: - const type_info *fType; //Actual typeid of the proxy - const type_info *fLastType; //Last used subtype - TClass *fClass; //Actual TClass - TClass *fLastClass; //Last used TClass - Char_t fSubTypes[72]; //map of known sub-types - Bool_t fVirtual; //Flag if class is virtual - void *fContext; //Optional user contex - Bool_t fInit; //Initialization flag + const type_info *fType; //Actual typeid of the proxy + std::atomic fClass; //Actual TClass + std::atomic fLast; //points into fSubTypes map for last used values + Char_t fSubTypes[72]; //map of known sub-types + void *fContext; //Optional user contex + mutable std::atomic fSubTypesReaders; //number of readers of fSubTypes + std::atomic fSubTypesWriteLockTaken; //True if there is a writer + Bool_t fVirtual; //Flag if class is virtual + std::atomic fInit; //Initialization flag + void* FindSubType(const type_info*) const; + void* CacheSubType(const type_info*, TClass*); protected: - TIsAProxy(const TIsAProxy&); - TIsAProxy& operator=(const TIsAProxy&); + TIsAProxy(const TIsAProxy&) = delete; + TIsAProxy& operator=(const TIsAProxy&) = delete; public: // Standard initializing constructor diff --git a/core/meta/src/TIsAProxy.cxx b/core/meta/src/TIsAProxy.cxx index b67b2ae7f00e8..dc6788cf5168c 100644 --- a/core/meta/src/TIsAProxy.cxx +++ b/core/meta/src/TIsAProxy.cxx @@ -15,7 +15,7 @@ #include "TIsAProxy.h" #include - +#include ////////////////////////////////////////////////////////////////////////// // // @@ -32,60 +32,29 @@ namespace { // typeid( * (DynamicType*) void_ptr ); virtual ~DynamicType() {} }; -} -typedef std::map ClassMap_t; // Internal type map -inline ClassMap_t *GetMap(void* p) -{ - return (ClassMap_t*)p; + typedef std::map ClassMap_t; // Internal type map + inline ClassMap_t *GetMap(const void* p) + { + return (ClassMap_t*)p; + } + + inline ClassMap_t::value_type* ToPair(void*p) + { + return (ClassMap_t::value_type*)p; + } } //______________________________________________________________________________ TIsAProxy::TIsAProxy(const std::type_info& typ, void* ctxt) - : fType(&typ), fLastType(&typ), fClass(0), fLastClass(0), - fVirtual(false), fContext(ctxt), fInit(false) + : fType(&typ), fClass(nullptr), fLast(nullptr), fContext(ctxt), + fSubTypesReaders(0), fSubTypesWriteLockTaken(false), + fVirtual(false), fInit(false) { // Standard initializing constructor ::new(fSubTypes) ClassMap_t(); - if ( sizeof(ClassMap_t) > sizeof(fSubTypes) ) { - Fatal("TIsAProxy::TIsAProxy", - "Classmap size is badly adjusted: it needs %u instead of %u bytes.", - (UInt_t)sizeof(ClassMap_t), (UInt_t)sizeof(fSubTypes)); - } -} - -//______________________________________________________________________________ -TIsAProxy::TIsAProxy(const TIsAProxy& iap) : - TVirtualIsAProxy(iap), - fType(iap.fType), - fLastType(iap.fLastType), - fClass(iap.fClass), - fLastClass(iap.fLastClass), - fVirtual(iap.fVirtual), - fContext(iap.fContext), - fInit(iap.fInit) -{ - //copy constructor - for(Int_t i=0; i<72; i++) fSubTypes[i]=iap.fSubTypes[i]; -} - -//______________________________________________________________________________ -TIsAProxy& TIsAProxy::operator=(const TIsAProxy& iap) -{ - //assignement operator - if(this!=&iap) { - TVirtualIsAProxy::operator=(iap); - fType=iap.fType; - fLastType=iap.fLastType; - fClass=iap.fClass; - fLastClass=iap.fLastClass; - for(Int_t i=0; i<72; i++) fSubTypes[i]=iap.fSubTypes[i]; - fVirtual=iap.fVirtual; - fContext=iap.fContext; - fInit=iap.fInit; - } - return *this; + static_assert(sizeof(ClassMap_t)<=sizeof(fSubTypes), "ClassMap size is to large for array"); } //______________________________________________________________________________ @@ -102,8 +71,10 @@ TIsAProxy::~TIsAProxy() void TIsAProxy::SetClass(TClass *cl) { // Set class pointer + // This method is not thread safe GetMap(fSubTypes)->clear(); - fClass = fLastClass = cl; + fClass = cl; + fLast = nullptr; } //______________________________________________________________________________ @@ -112,38 +83,85 @@ TClass* TIsAProxy::operator()(const void *obj) // IsA callback if ( !fInit ) { + if ( !fClass && fType ) { + auto cls = TClass::GetClass(*fType); + TClass* expected = nullptr; + fClass.compare_exchange_strong(expected,cls); + } + if ( !fClass) return nullptr; + fVirtual = (*fClass).ClassProperty() & kClassHasVirtual; fInit = kTRUE; - if ( !fClass && fType ) fClass = TClass::GetClass(*fType); - if ( !fClass) return 0; - fVirtual = fClass->ClassProperty() & kClassHasVirtual; } if ( !obj || !fVirtual ) { - return fClass; - } else { - // Avoid the case that the first word is a virtual_base_offset_table instead of - // a virtual_function_table - Long_t offset = **(Long_t**)obj; - if ( offset == 0 ) return fClass; - - DynamicType* ptr = (DynamicType*)obj; - const std::type_info* typ = &typeid(*ptr); - - if ( typ == fType ) { - return fClass; - } - else if ( typ == fLastType ) { - return fLastClass; - } - // Check if type is already in sub-class cache - else if ( 0 != (fLastClass=(*GetMap(fSubTypes))[long(typ)]) ) { - fLastType = typ; - } - // Last resort: lookup root class - else { - fLastClass = TClass::GetClass(*typ); - fLastType = typ; - (*GetMap(fSubTypes))[long(fLastType)] = fLastClass; - } + return fClass.load(); + } + // Avoid the case that the first word is a virtual_base_offset_table instead of + // a virtual_function_table + Long_t offset = **(Long_t**)obj; + if ( offset == 0 ) return fClass.load(); + + DynamicType* ptr = (DynamicType*)obj; + const std::type_info* typ = &typeid(*ptr); + + if ( typ == fType ) { + return fClass.load(); + } + auto last = ToPair(fLast.load()); + if ( last && typ == last->first ) { + return last->second; + } + // Check if type is already in sub-class cache + if ( nullptr == (last = ToPair(FindSubType(typ)) ) ) { + + // Last resort: lookup root class + auto cls = TClass::GetClass(*typ); + last = ToPair(CacheSubType(typ,cls)); + } + fLast.store(last); + + return last == nullptr? nullptr: last->second; +} +//______________________________________________________________________________ +inline void* TIsAProxy::FindSubType(const type_info* type) const +{ + bool needToWait = true; + do { + ++fSubTypesReaders; + + //See if there is a writer, if there is we need to release + // our reader count so that the writer can proceed + if(fSubTypesWriteLockTaken) { + --fSubTypesReaders; + while(fSubTypesWriteLockTaken) {} + } else { + needToWait = false; + } + } while(needToWait); + + void* returnValue =nullptr; + auto const map = GetMap(fSubTypes); + + auto found = map->find(type); + if(found != map->end()) { + returnValue = &(*found); } - return fLastClass; + --fSubTypesReaders; + return returnValue; +} + +//______________________________________________________________________________ +void* TIsAProxy::CacheSubType(const type_info* type, TClass* cls) +{ + //See if another thread has the write lock, wait if it does + bool expected = false; + while(not fSubTypesWriteLockTaken.compare_exchange_strong(expected,true) ) {expected = false;}; + + //See if there are any readers + while(fSubTypesReaders > 0); + + auto map = GetMap(fSubTypes); + auto ret = map->emplace(type,cls); + + fSubTypesWriteLockTaken = false; + return &(*(ret.first)); }