Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made TIsAProxy thread safe #49

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions core/meta/inc/TIsAProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#ifndef ROOT_Rtypes
#include "Rtypes.h"
#endif

#include <atomic>

class TClass;

Expand All @@ -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<TClass*> fClass; //Actual TClass
std::atomic<void*> 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<unsigned int> fSubTypesReaders; //number of readers of fSubTypes
std::atomic<bool> fSubTypesWriteLockTaken; //True if there is a writer
Bool_t fVirtual; //Flag if class is virtual
std::atomic<bool> 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
Expand Down
172 changes: 95 additions & 77 deletions core/meta/src/TIsAProxy.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "TIsAProxy.h"

#include <map>

#include <type_traits>

//////////////////////////////////////////////////////////////////////////
// //
Expand All @@ -32,60 +32,29 @@ namespace {
// typeid( * (DynamicType*) void_ptr );
virtual ~DynamicType() {}
};
}

typedef std::map<Long_t, TClass*> ClassMap_t; // Internal type map
inline ClassMap_t *GetMap(void* p)
{
return (ClassMap_t*)p;
typedef std::map<const void*, TClass*> 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");
}

//______________________________________________________________________________
Expand All @@ -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;
}

//______________________________________________________________________________
Expand All @@ -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));
}