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

Unloading AST optimization + change base class of TEnum from TNamed to TDictionary #57

Closed
Closed
8 changes: 6 additions & 2 deletions core/meta/inc/TDictionary.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,14 @@ class TDictionary : public TNamed {

private:
TDictAttributeMap *fAttributeMap; //pointer to a class attribute map
ULong64_t fUpdatingTransactionCount; //the Cling ID of the transaction that last updated the object

protected:
Bool_t InterpreterStateHasChanged();

public:
TDictionary(): fAttributeMap(0) { }
TDictionary(const char* name): TNamed(name, ""), fAttributeMap(0) { }
TDictionary(): fAttributeMap(0), fUpdatingTransactionCount(0) { }
TDictionary(const char* name): TNamed(name, ""), fAttributeMap(0), fUpdatingTransactionCount(0) { }
TDictionary(const TDictionary& dict);
virtual ~TDictionary();

Expand Down
5 changes: 2 additions & 3 deletions core/meta/inc/TEnum.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@
class TClass;
class TEnumConstant;

class TEnum : public TNamed {

typedef TDictionary::DeclId_t DeclId_t;
class TEnum : public TDictionary {

private:
THashList fConstantList; //list of constants the enum type
Expand All @@ -61,6 +59,7 @@ typedef TDictionary::DeclId_t DeclId_t;
}
DeclId_t GetDeclId() const { return (DeclId_t)fInfo; }
Bool_t IsValid();
Long_t Property() const;
void Update(DeclId_t id);

ClassDef(TEnum,2) //Enum type class
Expand Down
1 change: 1 addition & 0 deletions core/meta/inc/TInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class TInterpreter : public TNamed {

// core/meta helper functions.
virtual TMethodCall::EReturnType MethodCallReturnType(TFunction *func) const = 0;
virtual ULong64_t GetInterpreterStateMarker() const = 0;

typedef TDictionary::DeclId_t DeclId_t;
virtual DeclId_t GetDeclId(CallFunc_t *info) const = 0;
Expand Down
313 changes: 173 additions & 140 deletions core/meta/src/TCling.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -399,150 +399,13 @@ extern "C"
void TCling__UpdateListsOnCommitted(const cling::Transaction &T,
cling::Interpreter* interp) {

std::set<TClass*> modifiedTClasses; // TClasses that require update after this transaction

bool isTUTransaction = false;
if (T.decls_end()-T.decls_begin() == 1 && !T.hasNestedTransactions()) {
clang::Decl* FirstDecl = *(T.decls_begin()->m_DGR.begin());
if (clang::TranslationUnitDecl* TU
= dyn_cast<clang::TranslationUnitDecl>(FirstDecl)) {
// The is the first transaction, we have to expose to meta
// what's already in the AST.
isTUTransaction = true;

// FIXME: don't load the world. Really, don't. Maybe
// instead smarten TROOT::GetListOfWhateveros() which
// currently is a THashList but could be a
// TInterpreterLookupCollection, one that reimplements
// TCollection::FindObject(name) and performs a lookup
// if not found in its T(Hash)List.
cling::Interpreter::PushTransactionRAII RAII(interp);
for (clang::DeclContext::decl_iterator TUI = TU->decls_begin(),
TUE = TU->decls_end(); TUI != TUE; ++TUI)
((TCling*)gCling)->HandleNewDecl(*TUI, (*TUI)->isFromASTFile(),modifiedTClasses);
}
}

std::set<const void*> TransactionDeclSet;
if (!isTUTransaction && T.decls_end() - T.decls_begin()) {
const clang::Decl* WrapperFD = T.getWrapperFD();
for (cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
I != E; ++I) {
if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl)
continue;

for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI) {
if (*DI == WrapperFD)
continue;
TransactionDeclSet.insert(*DI);
((TCling*)gCling)->HandleNewDecl(*DI, false, modifiedTClasses);
}
}
}

// The above might trigger more decls to be deserialized.
// Thus the iteration over the deserialized decls must be last.
for (cling::Transaction::const_iterator I = T.deserialized_decls_begin(),
E = T.deserialized_decls_end(); I != E; ++I) {
for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI)
if (TransactionDeclSet.find(*DI) == TransactionDeclSet.end()) {
//FIXME: HandleNewDecl should take DeclGroupRef
((TCling*)gCling)->HandleNewDecl(*DI, /*isDeserialized*/true,
modifiedTClasses);
}
}


// When fully building the reflection info in TClass, a deserialization
// could be triggered, which may result in request for building the
// reflection info for the same TClass. This in turn will clear the caches
// for the TClass in-flight and cause null ptr derefs.
// FIXME: This is a quick fix, solving most of the issues. The actual
// question is: Shouldn't TClass provide a lock mechanism on update or lock
// itself until the update is done.
//
std::vector<TClass*> modifiedTClassesDiff(modifiedTClasses.size());
std::vector<TClass*>::iterator it;
it = set_difference(modifiedTClasses.begin(), modifiedTClasses.end(),
((TCling*)gCling)->GetModTClasses().begin(),
((TCling*)gCling)->GetModTClasses().end(),
modifiedTClassesDiff.begin());
modifiedTClassesDiff.resize(it - modifiedTClassesDiff.begin());

// Lock the TClass for updates
((TCling*)gCling)->GetModTClasses().insert(modifiedTClassesDiff.begin(),
modifiedTClassesDiff.end());
for (std::vector<TClass*>::const_iterator I = modifiedTClassesDiff.begin(),
E = modifiedTClassesDiff.end(); I != E; ++I) {
// Make sure the TClass has not been deleted.
if (!gROOT->GetListOfClasses()->FindObject(*I)) {
continue;
}
// Could trigger deserialization of decls.
cling::Interpreter::PushTransactionRAII RAII(interp);
// Unlock the TClass for updates
((TCling*)gCling)->GetModTClasses().erase(*I);

}
((TCling*)gCling)->UpdateListsOnCommitted(T, interp);
}

extern "C"
void TCling__UpdateListsOnUnloaded(const cling::Transaction &T) {
TGlobal *global = 0;
TFunction *function = 0;
TEnum* e = 0;
TListOfDataMembers* globals = (TListOfDataMembers*)gROOT->GetListOfGlobals();
TListOfFunctions* functions = (TListOfFunctions*)gROOT->GetListOfGlobalFunctions();
TListOfEnums* enums = (TListOfEnums*)gROOT->GetListOfEnums();
for(cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
I != E; ++I)
for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI) {
if (isa<VarDecl>(*DI) || isa<EnumConstantDecl>(*DI)) {
clang::ValueDecl* VD = dyn_cast<ValueDecl>(*DI);
global = (TGlobal*)globals->FindObject(VD->getNameAsString().c_str());
if (global && global->IsValid()) {
// Unload the global by setting the DataMemberInfo_t to 0
globals->Unload(global);
global->Update(0);
}
} else if (isa<RecordDecl>(*DI) || isa<NamespaceDecl>(*DI)) {
const clang::NamedDecl* ND = dyn_cast<NamedDecl>(*DI);
if (ND) {
std::string buf = ND->getNameAsString();
const char* name = buf.c_str();
TClass* cl = TClass::GetClass(name);
if (cl) {
cl->ResetClassInfo();
}
}
} else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(*DI)) {
function = gROOT->GetGlobalFunction(FD->getNameAsString().c_str());
if (function && function->IsValid()) {
functions->Unload(function);
function->Update(0);
}
} else if (const EnumDecl* ED = dyn_cast<EnumDecl>(*DI)) {
e = (TEnum*)enums->FindObject(ED->getNameAsString().c_str());
if (e && e->IsValid()) {
TIter iEnumConst(e->GetConstants());
while (TEnumConstant* enumConst = (TEnumConstant*)iEnumConst()) {
// Since the enum is already created and valid that ensures us that
// we have the enum constants created as well.
enumConst = (TEnumConstant*)globals->FindObject(enumConst->GetName());
if (enumConst && enumConst->IsValid()) {
globals->Unload(enumConst);
enumConst->Update(0);
}
}
enums->Unload(e);
e->Update(0);
}
}
}

((TCling*)gCling)->UpdateListsOnUnloaded(T);
}

extern "C"
Expand Down Expand Up @@ -878,7 +741,8 @@ namespace{
TCling::TCling(const char *name, const char *title)
: TInterpreter(name, title), fGlobalsListSerial(-1), fInterpreter(0),
fMetaProcessor(0), fNormalizedCtxt(0), fPrevLoadedDynLibInfo(0),
fClingCallbacks(0), fHaveSinglePCM(kFALSE), fAutoLoadCallBack(0)
fClingCallbacks(0), fHaveSinglePCM(kFALSE), fAutoLoadCallBack(0),
fTransactionCount(0)
{
// Initialize the cling interpreter interface.

Expand Down Expand Up @@ -2067,6 +1931,19 @@ void TCling::SetGetline(const char * (*getlineFunc)(const char* prompt),
#endif
}

//______________________________________________________________________________
void TCling::HandleNewTransaction(const cling::Transaction &T)
{
// Helper function to increase the internal Cling count of transactions
// that change the AST.
if ((std::distance(T.decls_begin(), T.decls_end()) != 1)
|| T.deserialized_decls_begin() != T.deserialized_decls_end()
|| T.macros_begin() != T.macros_end()
|| ((!T.getFirstDecl().isNull()) && ((*T.getFirstDecl().begin()) != T.getWrapperFD()))) {
fTransactionCount++;
}
}

//______________________________________________________________________________
void TCling::RecursiveRemove(TObject* obj)
{
Expand Down Expand Up @@ -4483,6 +4360,162 @@ void TCling::UpdateAllCanvases()
}
}

//______________________________________________________________________________
void TCling::UpdateListsOnCommitted(const cling::Transaction &T,
cling::Interpreter* interp) {

std::set<TClass*> modifiedTClasses; // TClasses that require update after this transaction

HandleNewTransaction(T);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we return early if HandleNewTransaction() didn't update the transaction count? I.e. could HandleNewTransaction() return a bool stating whether the transaction contains "something"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


bool isTUTransaction = false;
if (T.decls_end()-T.decls_begin() == 1 && !T.hasNestedTransactions()) {
clang::Decl* FirstDecl = *(T.decls_begin()->m_DGR.begin());
if (clang::TranslationUnitDecl* TU
= dyn_cast<clang::TranslationUnitDecl>(FirstDecl)) {
// The is the first transaction, we have to expose to meta
// what's already in the AST.
isTUTransaction = true;

// FIXME: don't load the world. Really, don't. Maybe
// instead smarten TROOT::GetListOfWhateveros() which
// currently is a THashList but could be a
// TInterpreterLookupCollection, one that reimplements
// TCollection::FindObject(name) and performs a lookup
// if not found in its T(Hash)List.
cling::Interpreter::PushTransactionRAII RAII(interp);
for (clang::DeclContext::decl_iterator TUI = TU->decls_begin(),
TUE = TU->decls_end(); TUI != TUE; ++TUI)
((TCling*)gCling)->HandleNewDecl(*TUI, (*TUI)->isFromASTFile(),modifiedTClasses);
}
}

std::set<const void*> TransactionDeclSet;
if (!isTUTransaction && T.decls_end() - T.decls_begin()) {
const clang::Decl* WrapperFD = T.getWrapperFD();
for (cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
I != E; ++I) {
if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl)
continue;

for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI) {
if (*DI == WrapperFD)
continue;
TransactionDeclSet.insert(*DI);
((TCling*)gCling)->HandleNewDecl(*DI, false, modifiedTClasses);
}
}
}

// The above might trigger more decls to be deserialized.
// Thus the iteration over the deserialized decls must be last.
for (cling::Transaction::const_iterator I = T.deserialized_decls_begin(),
E = T.deserialized_decls_end(); I != E; ++I) {
for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI)
if (TransactionDeclSet.find(*DI) == TransactionDeclSet.end()) {
//FIXME: HandleNewDecl should take DeclGroupRef
((TCling*)gCling)->HandleNewDecl(*DI, /*isDeserialized*/true,
modifiedTClasses);
}
}


// When fully building the reflection info in TClass, a deserialization
// could be triggered, which may result in request for building the
// reflection info for the same TClass. This in turn will clear the caches
// for the TClass in-flight and cause null ptr derefs.
// FIXME: This is a quick fix, solving most of the issues. The actual
// question is: Shouldn't TClass provide a lock mechanism on update or lock
// itself until the update is done.
//
std::vector<TClass*> modifiedTClassesDiff(modifiedTClasses.size());
std::vector<TClass*>::iterator it;
it = set_difference(modifiedTClasses.begin(), modifiedTClasses.end(),
((TCling*)gCling)->GetModTClasses().begin(),
((TCling*)gCling)->GetModTClasses().end(),
modifiedTClassesDiff.begin());
modifiedTClassesDiff.resize(it - modifiedTClassesDiff.begin());

// Lock the TClass for updates
((TCling*)gCling)->GetModTClasses().insert(modifiedTClassesDiff.begin(),
modifiedTClassesDiff.end());
for (std::vector<TClass*>::const_iterator I = modifiedTClassesDiff.begin(),
E = modifiedTClassesDiff.end(); I != E; ++I) {
// Make sure the TClass has not been deleted.
if (!gROOT->GetListOfClasses()->FindObject(*I)) {
continue;
}
// Could trigger deserialization of decls.
cling::Interpreter::PushTransactionRAII RAII(interp);
// Unlock the TClass for updates
((TCling*)gCling)->GetModTClasses().erase(*I);

}
}

//______________________________________________________________________________
void TCling::UpdateListsOnUnloaded(const cling::Transaction &T)
{
HandleNewTransaction(T);

// Unload the objects from the lists and update the objects' state.
TGlobal *global = 0;
TFunction *function = 0;
TEnum* e = 0;
TListOfDataMembers* globals = (TListOfDataMembers*)gROOT->GetListOfGlobals();
TListOfFunctions* functions = (TListOfFunctions*)gROOT->GetListOfGlobalFunctions();
TListOfEnums* enums = (TListOfEnums*)gROOT->GetListOfEnums();
for(cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
I != E; ++I)
for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(),
DE = I->m_DGR.end(); DI != DE; ++DI) {
if (isa<VarDecl>(*DI) || isa<EnumConstantDecl>(*DI)) {
clang::ValueDecl* VD = dyn_cast<ValueDecl>(*DI);
global = (TGlobal*)globals->FindObject(VD->getNameAsString().c_str());
if (global && global->IsValid()) {
// Unload the global by setting the DataMemberInfo_t to 0
globals->Unload(global);
global->Update(0);
}
} else if (isa<RecordDecl>(*DI) || isa<NamespaceDecl>(*DI)) {
const clang::NamedDecl* ND = dyn_cast<NamedDecl>(*DI);
if (ND) {
std::string buf = ND->getNameAsString();
const char* name = buf.c_str();
TClass* cl = TClass::GetClass(name);
if (cl) {
cl->ResetClassInfo();
}
}
} else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(*DI)) {
function = gROOT->GetGlobalFunction(FD->getNameAsString().c_str());
if (function && function->IsValid()) {
functions->Unload(function);
function->Update(0);
}
} else if (const EnumDecl* ED = dyn_cast<EnumDecl>(*DI)) {
e = (TEnum*)enums->FindObject(ED->getNameAsString().c_str());
if (e && e->IsValid()) {
TIter iEnumConst(e->GetConstants());
while (TEnumConstant* enumConst = (TEnumConstant*)iEnumConst()) {
// Since the enum is already created and valid that ensures us that
// we have the enum constants created as well.
enumConst = (TEnumConstant*)globals->FindObject(enumConst->GetName());
if (enumConst) {
globals->Unload(enumConst);
enumConst->Update(0);
}
}
enums->Unload(e);
e->Update(0);
}
}
}
}


//______________________________________________________________________________
const char* TCling::GetSharedLibs()
{
Expand Down
Loading