Description: fix engine leaks/crashes when lot of autonomous transactions have
 been started and finished
Bug: http://tracker.firebirdsql.org/browse/CORE-3908
Origin: upstream, http://sourceforge.net/p/firebird/code/57707/
Origin: upstream, http://sourceforge.net/p/firebird/code/57710/
--- a/src/jrd/tra.cpp
+++ b/src/jrd/tra.cpp
@@ -1608,7 +1608,7 @@ jrd_tra* TRA_start(thread_db* tdbb, ULON
 	// To handle the problems of relation locks, allocate a temporary
 	// transaction block first, seize relation locks, then go ahead and
 	// make up the real transaction block.
-	MemoryPool* const pool = outer ? outer->tra_pool : dbb->createPool();
+	MemoryPool* const pool = outer ? outer->getAutonomousPool() : dbb->createPool();
 	Jrd::ContextPoolHolder context(tdbb, pool);
 	jrd_tra* const temp = jrd_tra::create(pool, attachment, outer);
 
@@ -1663,7 +1663,7 @@ jrd_tra* TRA_start(thread_db* tdbb, int
 	// To handle the problems of relation locks, allocate a temporary
 	// transaction block first, seize relation locks, then go ahead and
 	// make up the real transaction block.
-	MemoryPool* const pool = outer ? outer->tra_pool : dbb->createPool();
+	MemoryPool* const pool = outer ? outer->getAutonomousPool() : dbb->createPool();
 	Jrd::ContextPoolHolder context(tdbb, pool);
 	jrd_tra* const temp = jrd_tra::create(pool, attachment, outer);
 
@@ -3617,8 +3617,17 @@ jrd_tra::~jrd_tra()
 	{
 		delete tra_blob_space;
 	}
+	else
+	{
+		fb_assert(!tra_arrays);
+	}
 
 	DFW_delete_deferred(this, -1);
+
+	if (tra_autonomous_pool)
+	{
+		MemoryPool::deletePool(tra_autonomous_pool);
+	}
 }
 
 
@@ -3632,6 +3641,49 @@ UserManagement* jrd_tra::getUserManageme
 }
 
 
+jrd_tra* jrd_tra::getOuter()
+{
+	jrd_tra* tra = this;
+
+	while (tra->tra_outer)
+	{
+		tra = tra->tra_outer;
+	}
+
+	return tra;
+}
+
+
+MemoryPool* jrd_tra::getAutonomousPool()
+{
+	if (!tra_autonomous_pool)
+	{
+		MemoryPool* pool = tra_pool;
+		jrd_tra* outer = tra_outer;
+		while (outer)
+		{
+			pool = outer->tra_pool;
+			outer = outer->tra_outer;
+		}
+		tra_autonomous_pool = MemoryPool::createPool(pool, tra_memory_stats);
+		tra_autonomous_cnt = 0;
+	}
+
+	return tra_autonomous_pool;
+}
+
+
+void jrd_tra::releaseAutonomousPool(MemoryPool* toRelease)
+{
+	fb_assert(tra_autonomous_pool == toRelease);
+	if (++tra_autonomous_cnt > TRA_AUTONOMOUS_PER_POOL)
+	{
+		MemoryPool::deletePool(tra_autonomous_pool);
+		tra_autonomous_pool = NULL;
+	}
+}
+
+
 /// class TraceSweepEvent
 
 TraceSweepEvent::TraceSweepEvent(thread_db* tdbb) :
--- a/src/jrd/tra.h
+++ b/src/jrd/tra.h
@@ -115,7 +115,8 @@ public:
 		tra_pool(p),
 		tra_memory_stats(parent_stats),
 		tra_blobs_tree(p),
-		tra_blobs(&tra_blobs_tree),
+		tra_blobs(outer ? outer->tra_blobs : &tra_blobs_tree),
+		tra_arrays(NULL),
 		tra_deferred_job(NULL),
 		tra_resources(*p),
 		tra_context_vars(*p),
@@ -129,15 +130,10 @@ public:
 		tra_blob_space(NULL),
 		tra_undo_space(NULL),
 		tra_undo_record(NULL),
-		tra_user_management(NULL)
+		tra_user_management(NULL),
+		tra_autonomous_pool(NULL),
+		tra_autonomous_cnt(0)
 	{
-		if (outer)
-		{
-			fb_assert(p == outer->tra_pool);
-			tra_arrays = outer->tra_arrays;
-			tra_blobs = outer->tra_blobs;
-		}
-
 		tra_transactions.resize(length);
 	}
 
@@ -162,7 +158,10 @@ public:
 		{
 			if (transaction->tra_outer)
 			{
+				jrd_tra* outer = transaction->tra_outer;
+				MemoryPool* const pool = transaction->tra_pool;
 				delete transaction;
+				outer->releaseAutonomousPool(pool);
 			}
 			else
 			{
@@ -224,8 +223,15 @@ private:
 
 	Record* tra_undo_record;	// temporary record used for the undo purposes
 	UserManagement* tra_user_management;
+	MemoryPool* tra_autonomous_pool;
+	USHORT tra_autonomous_cnt;
+	static const USHORT TRA_AUTONOMOUS_PER_POOL = 64;
 
 public:
+	MemoryPool* getAutonomousPool();
+	void releaseAutonomousPool(MemoryPool* toRelease);
+	jrd_tra* getOuter();
+
 	SSHORT getLockWait() const
 	{
 		return -tra_lock_timeout;
@@ -245,7 +251,10 @@ public:
 	TempSpace* getUndoSpace()
 	{
 		if (!tra_undo_space)
+		{
+			fb_assert(!tra_outer);
 			tra_undo_space = FB_NEW(*tra_pool) TempSpace(*tra_pool, TRA_UNDO_SPACE);
+		}
 
 		return tra_undo_space;
 	}
--- a/src/jrd/blb.cpp
+++ b/src/jrd/blb.cpp
@@ -268,6 +268,8 @@ blb* BLB_create2(thread_db* tdbb,
  *      Basically BLB_create() with BPB structure.
  *
  **************************************/
+	transaction = transaction->getOuter();
+
 	SET_TDBB(tdbb);
 	Database* dbb = tdbb->getDatabase();
 	CHECK_DBB(dbb);
@@ -282,6 +284,7 @@ blb* BLB_create2(thread_db* tdbb,
 						   reinterpret_cast<USHORT*>(&from_charset),	// safe - alignment not changed
 						   reinterpret_cast<USHORT*>(&to_charset),		// safe - alignment not changed
 						   NULL, NULL, NULL, NULL);
+
 	blb* blob = allocate_blob(tdbb, transaction);
 
 	if (type & isc_bpb_type_stream)
@@ -541,6 +544,8 @@ blb* BLB_get_array(thread_db* tdbb, jrd_
  *      Get array blob and array descriptor.
  *
  **************************************/
+	transaction = transaction->getOuter();
+
 	SET_TDBB(tdbb);
 
 	blb* blob = BLB_open2(tdbb, transaction, blob_id, 0, 0);
@@ -818,6 +823,8 @@ SLONG BLB_get_slice(thread_db* tdbb,
  *      Fetch a slice of an array.
  *
  **************************************/
+	transaction = transaction->getOuter();
+
 	SET_TDBB(tdbb);
     //Database* database = GET_DBB();
 	Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool);
@@ -1069,6 +1076,7 @@ void BLB_move(thread_db* tdbb, dsc* from
 
 	CLEAR_NULL(record, id);
 	jrd_tra* transaction = request->req_transaction;
+	transaction = transaction->getOuter();
 
 	// If the target is a view, this must be from a view update trigger.
 	// Just pass the blob id thru.
@@ -1268,6 +1276,8 @@ blb* BLB_open2(thread_db* tdbb,
  *      Basically BLB_open() with BPB structure.
  *
  **************************************/
+	transaction = transaction->getOuter();
+
 	SET_TDBB(tdbb);
 	Database* dbb = tdbb->getDatabase();
 
@@ -1650,6 +1660,8 @@ void BLB_put_slice(	thread_db*	tdbb,
  *      Put a slice of an array.
  *
  **************************************/
+	transaction = transaction->getOuter();
+
 	SET_TDBB(tdbb);
 	Jrd::ContextPoolHolder context(tdbb, transaction->tra_pool);
 
@@ -1819,6 +1831,7 @@ void BLB_scalar(thread_db*		tdbb,
  * Functional description
  *
  **************************************/
+	transaction = transaction->getOuter();
 
 	SLONG stuff[IAD_LEN(16) / 4];
 
@@ -1867,6 +1880,7 @@ static ArrayField* alloc_array(jrd_tra*
  *      Allocate an array block based on a prototype array descriptor.
  *
  **************************************/
+	fb_assert(!transaction->tra_outer);
 
 	// Compute size and allocate block
 
@@ -1904,14 +1918,11 @@ static blb* allocate_blob(thread_db* tdb
  *      Create a shiney, new, empty blob.
  *
  **************************************/
+	fb_assert(!transaction->tra_outer);
 
 	SET_TDBB(tdbb);
 	Database* dbb = tdbb->getDatabase();
 
-	// If we are in an autonomous transaction, link the blob on the transaction started by the user.
-	while (transaction->tra_outer)
-		transaction = transaction->tra_outer;
-
 	// Create a blob large enough to hold a single data page.
 
 	blb* blob = FB_NEW(*transaction->tra_pool) blb(*transaction->tra_pool, dbb->dbb_page_size);
@@ -1959,7 +1970,11 @@ static ISC_STATUS blob_filter(USHORT	act
 
 	thread_db* tdbb = JRD_get_thread_data();
 
-	jrd_tra* const transaction = reinterpret_cast<jrd_tra*>(control->ctl_internal[1]);
+	jrd_tra* transaction = reinterpret_cast<jrd_tra*>(control->ctl_internal[1]);
+	if (transaction)
+	{
+		transaction = transaction->getOuter();
+	}
 	bid* blob_id = reinterpret_cast<bid*>(control->ctl_internal[2]);
 
 #ifdef DEV_BUILD
@@ -2492,6 +2507,7 @@ static void move_from_string(thread_db*
 
 	jrd_req* request = tdbb->getRequest();
 	jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction();
+	transaction = transaction->getOuter();
 
 	UCharBuffer bpb;
 	BLB_gen_bpb_from_descs(from_desc, to_desc, bpb);
@@ -2598,6 +2614,7 @@ static void move_to_string(thread_db* td
 
 	jrd_req* request = tdbb->getRequest();
 	jrd_tra* transaction = request ? request->req_transaction : tdbb->getTransaction();
+	transaction = transaction->getOuter();
 
 	UCharBuffer bpb;
 	BLB_gen_bpb_from_descs(fromDesc, &blobAsText, bpb);
--- a/src/jrd/exe.cpp
+++ b/src/jrd/exe.cpp
@@ -3255,6 +3255,7 @@ static void release_blobs(thread_db* tdb
 	if (transaction)
 	{
 		DEV_BLKCHK(transaction, type_tra);
+		transaction = transaction->getOuter();
 
 		/* Release blobs bound to this request */
 
