#include "TClass.h"
#include "TError.h"
#include "TInterpreter.h"
#include "TIsAProxy.h"
#include <map>
#include <type_traits>
namespace {
struct DynamicType {
virtual ~DynamicType() {}
};
typedef std::map<const void*, TClass*> ClassMap_t;
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)
: fType(&typ), fClass(nullptr), fLast(nullptr),
fSubTypesReaders(0), fSubTypesWriteLockTaken(kFALSE),
fVirtual(kFALSE), fInit(kFALSE)
{
static_assert(sizeof(ClassMap_t)<=sizeof(fSubTypes), "ClassMap size is to large for array");
::new(fSubTypes) ClassMap_t();
}
TIsAProxy::~TIsAProxy()
{
ClassMap_t* m = GetMap(fSubTypes);
m->clear();
m->~ClassMap_t();
}
void TIsAProxy::SetClass(TClass *cl)
{
GetMap(fSubTypes)->clear();
fClass = cl;
fLast = nullptr;
}
TClass* TIsAProxy::operator()(const void *obj)
{
if ( !fInit ) {
if ( !fClass.load() && fType ) {
auto cls = TClass::GetClass(*fType);
TClass* expected = nullptr;
fClass.compare_exchange_strong(expected,cls);
}
if ( !fClass.load() ) return nullptr;
fVirtual = (*fClass).ClassProperty() & kClassHasVirtual;
fInit = kTRUE;
}
if ( !obj || !fVirtual ) {
return fClass.load();
}
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;
}
last = ToPair(FindSubType(typ));
if ( last == nullptr || last->second == nullptr ) {
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 = kTRUE;
do {
++fSubTypesReaders;
if(fSubTypesWriteLockTaken) {
--fSubTypesReaders;
while(fSubTypesWriteLockTaken) {}
} else {
needToWait = kFALSE;
}
} while(needToWait);
void* returnValue = nullptr;
auto const map = GetMap(fSubTypes);
auto found = map->find(type);
if(found != map->end()) {
returnValue = &(*found);
}
--fSubTypesReaders;
return returnValue;
}
void* TIsAProxy::CacheSubType(const type_info* type, TClass* cls)
{
Bool_t expected = kFALSE;
while(! fSubTypesWriteLockTaken.compare_exchange_strong(expected,kTRUE) ) {
expected = kFALSE;
};
while(fSubTypesReaders > 0);
auto map = GetMap(fSubTypes);
auto ret = map->emplace(type,cls);
if (!ret.second) {
(*ret.first).second = cls;
}
fSubTypesWriteLockTaken = kFALSE;
return &(*(ret.first));
}