Fix TransactionPool placement new and delete.
TransactionPool was using placement new and delete improperly which could lead to Transaction::~Transaction running twice. Make Transaction constructor private to make sure TransactionPool has allocated all Transactions it contains. Remove "*Very useful for debugging purposes" leak on every allocation.
This commit is contained in:
parent
c2736a6cc2
commit
c6b24d764c
@ -39,6 +39,7 @@ namespace llvm {
|
|||||||
|
|
||||||
namespace cling {
|
namespace cling {
|
||||||
class IncrementalExecutor;
|
class IncrementalExecutor;
|
||||||
|
class TransactionPool;
|
||||||
|
|
||||||
///\brief Contains information about the consumed input at once.
|
///\brief Contains information about the consumed input at once.
|
||||||
///
|
///
|
||||||
@ -184,14 +185,20 @@ namespace cling {
|
|||||||
///
|
///
|
||||||
clang::FileID m_BufferFID;
|
clang::FileID m_BufferFID;
|
||||||
|
|
||||||
public:
|
///\brief This is all to support allocation via TransactionPool.
|
||||||
|
/// There is currently no way for TransactionPool to mark if a Transaction
|
||||||
|
/// originated there or elsewhere, so if this interface is needed
|
||||||
|
/// then TransactionPool will have to be rewritten (or even removed
|
||||||
|
/// as the performance gains seem negligable)
|
||||||
|
///
|
||||||
|
friend class TransactionPool;
|
||||||
Transaction(clang::Sema& S);
|
Transaction(clang::Sema& S);
|
||||||
Transaction(const CompilationOptions& Opts, clang::Sema& S);
|
Transaction(const CompilationOptions& Opts, clang::Sema& S);
|
||||||
|
~Transaction();
|
||||||
|
|
||||||
void Initialize(clang::Sema& S);
|
void Initialize(clang::Sema& S);
|
||||||
|
|
||||||
~Transaction();
|
public:
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
kCollecting,
|
kCollecting,
|
||||||
@ -511,7 +518,6 @@ namespace cling {
|
|||||||
|
|
||||||
void printStructureBrief(size_t nindent = 0) const;
|
void printStructureBrief(size_t nindent = 0) const;
|
||||||
|
|
||||||
friend class TransactionPool;
|
|
||||||
private:
|
private:
|
||||||
bool comesFromASTReader(clang::DeclGroupRef DGR) const;
|
bool comesFromASTReader(clang::DeclGroupRef DGR) const;
|
||||||
};
|
};
|
||||||
|
@ -187,7 +187,7 @@ namespace cling {
|
|||||||
void
|
void
|
||||||
IncrementalParser::Initialize(llvm::SmallVectorImpl<ParseResultTransaction>&
|
IncrementalParser::Initialize(llvm::SmallVectorImpl<ParseResultTransaction>&
|
||||||
result, bool isChildInterpreter) {
|
result, bool isChildInterpreter) {
|
||||||
m_TransactionPool.reset(new TransactionPool(getCI()->getSema()));
|
m_TransactionPool.reset(new TransactionPool);
|
||||||
if (hasCodeGenerator()) {
|
if (hasCodeGenerator()) {
|
||||||
getCodeGenerator()->Initialize(getCI()->getASTContext());
|
getCodeGenerator()->Initialize(getCI()->getASTContext());
|
||||||
m_BackendPasses.reset(new BackendPasses(getCI()->getCodeGenOpts(),
|
m_BackendPasses.reset(new BackendPasses(getCI()->getCodeGenOpts(),
|
||||||
@ -261,17 +261,16 @@ namespace cling {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IncrementalParser::~IncrementalParser() {
|
IncrementalParser::~IncrementalParser() {
|
||||||
const Transaction* T = getFirstTransaction();
|
Transaction* T = const_cast<Transaction*>(getFirstTransaction());
|
||||||
const Transaction* nextT = 0;
|
|
||||||
while (T) {
|
while (T) {
|
||||||
assert((T->getState() == Transaction::kCommitted
|
assert((T->getState() == Transaction::kCommitted
|
||||||
|| T->getState() == Transaction::kRolledBackWithErrors
|
|| T->getState() == Transaction::kRolledBackWithErrors
|
||||||
|| T->getState() == Transaction::kNumStates // reset from the pool
|
|| T->getState() == Transaction::kNumStates // reset from the pool
|
||||||
|| T->getState() == Transaction::kRolledBack)
|
|| T->getState() == Transaction::kRolledBack)
|
||||||
&& "Not committed?");
|
&& "Not committed?");
|
||||||
nextT = T->getNext();
|
const Transaction* nextT = T->getNext();
|
||||||
delete T;
|
m_TransactionPool->releaseTransaction(T, false);
|
||||||
T = nextT;
|
T = const_cast<Transaction*>(nextT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +286,7 @@ namespace cling {
|
|||||||
Transaction* IncrementalParser::beginTransaction(const CompilationOptions&
|
Transaction* IncrementalParser::beginTransaction(const CompilationOptions&
|
||||||
Opts) {
|
Opts) {
|
||||||
Transaction* OldCurT = m_Consumer->getTransaction();
|
Transaction* OldCurT = m_Consumer->getTransaction();
|
||||||
Transaction* NewCurT = m_TransactionPool->takeTransaction();
|
Transaction* NewCurT = m_TransactionPool->takeTransaction(m_CI->getSema());
|
||||||
NewCurT->setCompilationOpts(Opts);
|
NewCurT->setCompilationOpts(Opts);
|
||||||
// If we are in the middle of transaction and we see another begin
|
// If we are in the middle of transaction and we see another begin
|
||||||
// transaction - it must be nested transaction.
|
// transaction - it must be nested transaction.
|
||||||
|
@ -30,62 +30,48 @@ namespace cling {
|
|||||||
//
|
//
|
||||||
llvm::SmallVector<Transaction*, POOL_SIZE> m_Transactions;
|
llvm::SmallVector<Transaction*, POOL_SIZE> m_Transactions;
|
||||||
|
|
||||||
///\brief The Sema required by cling::Transactions' ctor.
|
|
||||||
///
|
|
||||||
clang::Sema& m_Sema;
|
|
||||||
|
|
||||||
// We need to free them in blocks.
|
|
||||||
//
|
|
||||||
//llvm::SmallVector<Transaction*, 64> m_TransactionBlocks;
|
|
||||||
#ifndef NDEBUG
|
|
||||||
bool m_Debug;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
public:
|
public:
|
||||||
TransactionPool(clang::Sema& S) : m_Sema(S) {
|
TransactionPool() {}
|
||||||
#ifndef NDEBUG
|
|
||||||
m_Debug = false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
~TransactionPool() {
|
~TransactionPool() {
|
||||||
for (size_t i = 0, e = m_Transactions.size(); i < e; ++i)
|
// Only free the memory as anything put in m_Transactions will have
|
||||||
delete m_Transactions[i];
|
// already been destructed in releaseTransaction
|
||||||
|
for (Transaction* T : m_Transactions)
|
||||||
|
::operator delete(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction* takeTransaction() {
|
Transaction* takeTransaction(clang::Sema& S) {
|
||||||
if (m_Transactions.empty())
|
Transaction *T;
|
||||||
return new Transaction(m_Sema);
|
if (kDebugMode || m_Transactions.empty()) {
|
||||||
Transaction* T = new (m_Transactions.pop_back_val()) Transaction(m_Sema);
|
T = (Transaction*) ::operator new(sizeof(Transaction));
|
||||||
#ifndef NDEBUG
|
new(T) Transaction(S);
|
||||||
// *Very useful for debugging purposes and setting breakpoints in gdb.
|
} else
|
||||||
if (m_Debug)
|
T = new (m_Transactions.pop_back_val()) Transaction(S);
|
||||||
T = new Transaction(m_Sema);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
T->m_State = Transaction::kCollecting;
|
|
||||||
return T;
|
return T;
|
||||||
}
|
}
|
||||||
|
|
||||||
void releaseTransaction(Transaction* T) {
|
void releaseTransaction(Transaction* T, bool reuse = true) {
|
||||||
assert((T->getState() == Transaction::kCompleted ||
|
if (reuse) {
|
||||||
T->getState() == Transaction::kRolledBack)
|
assert((T->getState() == Transaction::kCompleted ||
|
||||||
&& "Transaction must completed!");
|
T->getState() == Transaction::kRolledBack)
|
||||||
|
&& "Transaction must completed!");
|
||||||
|
}
|
||||||
|
|
||||||
// Tell the parent that T is gone.
|
// Tell the parent that T is gone.
|
||||||
if (T->getParent())
|
if (T->getParent())
|
||||||
T->getParent()->removeNestedTransaction(T);
|
T->getParent()->removeNestedTransaction(T);
|
||||||
|
|
||||||
if (m_Transactions.size() == POOL_SIZE) {
|
|
||||||
// don't overflow the pool
|
|
||||||
delete T;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
T->m_State = Transaction::kNumStates;
|
|
||||||
T->~Transaction();
|
T->~Transaction();
|
||||||
m_Transactions.push_back(T);
|
|
||||||
|
// don't overflow the pool
|
||||||
|
if (reuse && (m_Transactions.size() < POOL_SIZE)) {
|
||||||
|
T->m_State = Transaction::kNumStates;
|
||||||
|
m_Transactions.push_back(T);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
::operator delete(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef POOL_SIZE
|
#undef POOL_SIZE
|
||||||
#undef TRANSACTIONS_IN_BLOCK
|
#undef TRANSACTIONS_IN_BLOCK
|
||||||
};
|
};
|
||||||
|
15
test/CodeUnloading/TransactionPool.C
Normal file
15
test/CodeUnloading/TransactionPool.C
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CLING - the C++ LLVM-based InterpreterG :)
|
||||||
|
//
|
||||||
|
// This file is dual-licensed: you can choose to license it under the University
|
||||||
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
||||||
|
// LICENSE.TXT for details.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// RUN: cat %s | %cling -I %S -Xclang -verify
|
||||||
|
// Test transactionPoolReuse
|
||||||
|
|
||||||
|
.undo
|
||||||
|
|
||||||
|
//expected-no-diagnostics
|
||||||
|
.q
|
Loading…
Reference in New Issue
Block a user