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:
Frederich Munch 2016-06-26 19:43:40 -04:00 committed by sftnight
parent c2736a6cc2
commit c6b24d764c
4 changed files with 58 additions and 52 deletions

View File

@ -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;
}; };

View File

@ -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.

View File

@ -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
}; };

View 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