8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 08:03:04 +01:00

Postfix for CORE-3935 & CORE-3944: helps avoid segfaults/deadlocks when shutting down firebird

This commit is contained in:
alexpeshkoff 2013-08-16 12:44:10 +00:00
parent 70a5c5db43
commit a8f8465366
6 changed files with 225 additions and 147 deletions

View File

@ -145,11 +145,11 @@ public:
{
// first of all try to enter the mutex
// this will help to make sure it's not locked by other thread
if (!tryEnter("assertLocked()"))
if (!tryEnter(FB_FUNCTION))
{
return false;
}
// make sure mutex was already locked prior assertLocked
// make sure mutex was already locked
bool rc = lockCount > 1;
// leave to release lock, done by us in tryEnter
leave();
@ -158,13 +158,6 @@ public:
}
#endif
void assertLocked()
{
#ifdef DEV_BUILD
fb_assert(locked());
#endif
}
public:
static void initMutexes() { }
@ -273,11 +266,11 @@ public:
{
// first of all try to enter the mutex
// this will help to make sure it's not locked by other thread
if (!tryEnter("assertLocked()"))
if (!tryEnter(FB_FUNCTION))
{
return false;
}
// make sure mutex was already locked prior assertLocked
// make sure mutex was already locked
bool rc = lockCount > 1;
// leave to release lock, done by us in tryEnter
leave();
@ -286,13 +279,6 @@ public:
}
#endif
void assertLocked()
{
#ifdef DEV_BUILD
fb_assert(locked());
#endif
}
public:
static void initMutexes();

View File

@ -367,6 +367,8 @@ const ULONG ATT_gfix_attachment = 0x01000L; // Indicate a GFIX attachment
const ULONG ATT_gstat_attachment = 0x02000L; // Indicate a GSTAT attachment
const ULONG ATT_no_db_triggers = 0x04000L; // Don't execute database triggers
const ULONG ATT_manual_lock = 0x08000L; // Was locked manually
const ULONG ATT_async_manual_lock = 0x10000L; // Async mutex was locked manually
const ULONG ATT_purge_started = 0x20000L; // Purge already started - avoid 2 purges at once
const ULONG ATT_NO_CLEANUP = (ATT_no_cleanup | ATT_notify_gc);

View File

@ -70,7 +70,7 @@ public:
return jAtt;
}
blb* getHandle()
blb* getHandle() throw()
{
return blob;
}
@ -107,7 +107,7 @@ public:
{
}
jrd_tra* getHandle()
jrd_tra* getHandle() throw()
{
return transaction;
}
@ -170,7 +170,7 @@ public:
JAttachment* getAttachment();
// Change after adding separate handle for cursor in dsql
dsql_req* getHandle();
dsql_req* getHandle() throw();
private:
Firebird::RefPtr<JStatement> statement;
@ -210,7 +210,7 @@ public:
return jAtt;
}
dsql_req* getHandle()
dsql_req* getHandle() throw()
{
return statement;
}
@ -229,7 +229,7 @@ inline JAttachment* JResultSet::getAttachment()
}
// Change after adding separate handle for cursor in dsql
inline dsql_req* JResultSet::getHandle()
inline dsql_req* JResultSet::getHandle() throw()
{
return statement->getHandle();
}
@ -263,7 +263,7 @@ public:
return jAtt;
}
JrdStatement* getHandle()
JrdStatement* getHandle() throw()
{
return rq;
}
@ -288,7 +288,7 @@ public:
{
}
JEvents* getHandle()
JEvents* getHandle() throw()
{
return this;
}
@ -355,7 +355,7 @@ public:
public:
explicit JAttachment(Attachment* handle);
Attachment* getHandle()
Attachment* getHandle() throw()
{
return att;
}
@ -365,9 +365,9 @@ public:
return this;
}
Firebird::Mutex* getMutex(bool useAsync = false)
Firebird::Mutex* getMutex(bool useAsync = false, bool forceAsync = false)
{
if (useAsync)
if (useAsync && (!forceAsync))
{
fb_assert(!mainMutex.locked());
}
@ -376,8 +376,8 @@ public:
void cancel()
{
asyncMutex.assertLocked();
mainMutex.assertLocked();
fb_assert(asyncMutex.locked());
fb_assert(mainMutex.locked());
att = NULL;
}
@ -387,9 +387,12 @@ public:
void manualLock(ULONG& flags);
void manualUnlock(ULONG& flags);
void manualAsyncUnlock(ULONG& flags);
private:
Attachment* att;
// This mutexes guarantee attachment existence. After releasing both of them with possibly
// zero att_use_count one should check does attachment still exists calling getHandle().
Firebird::Mutex mainMutex, asyncMutex;
void freeEngineData(Firebird::IStatus* status);

View File

@ -250,7 +250,7 @@ void JAttachment::manualLock(ULONG& flags)
fb_assert(!(flags & ATT_manual_lock));
asyncMutex.enter(FB_FUNCTION);
mainMutex.enter(FB_FUNCTION);
flags |= ATT_manual_lock;
flags |= (ATT_manual_lock | ATT_async_manual_lock);
}
void JAttachment::manualUnlock(ULONG& flags)
@ -259,6 +259,15 @@ void JAttachment::manualUnlock(ULONG& flags)
{
flags &= ~ATT_manual_lock;
mainMutex.leave();
}
manualAsyncUnlock(flags);
}
void JAttachment::manualAsyncUnlock(ULONG& flags)
{
if (flags & ATT_async_manual_lock)
{
flags &= ~ATT_async_manual_lock;
asyncMutex.leave();
}
}
@ -371,15 +380,21 @@ namespace
{
using Jrd::Attachment;
// Flag engineShutdown guarantees that no new attachment is created after setting it
// and helps avoid more than 1 shutdown threads running simultaneously.
bool engineShutdown = false;
// This flag is protected with 2 mutexes. shutdownMutex is taken by each shutdown thread
// (for a relatively long time). newAttachmentMutex is taken (for a short time) when
// shutdown thread is starting shutdown and also when new attachment is created.
GlobalPtr<Mutex> shutdownMutex, newAttachmentMutex;
// This mutex is set when new Database block is created. It's global first of all to satisfy
// SS requirement - avoid 2 Database blocks for same database (file). Also guarantees no
// half-done Database block in databases linked list. Always taken before databases_mutex.
GlobalPtr<Mutex> db_init_mutex;
GlobalPtr<Mutex> dbInitMutex;
Database* databases = NULL;
bool engineShuttingDown = false;
// This mutex protects both linked list of databases and flag engineShuttingDown.
// Flag engineShuttingDown guarantees that no new attachment is created after setting it.
// This mutex protects linked list of databases
GlobalPtr<Mutex> databases_mutex;
// Holder for per-database init/fini mutex
@ -451,7 +466,7 @@ namespace
// attachments) is accessed. No other mutex from above mentioned here can be taken after
// dbb_sync with an exception of attachment mutex for new attachment.
// So finally the order of taking mutexes is:
// 1. db_init_mutex (in attach/create database) or attachment mutex in other entries
// 1. dbInitMutex (in attach/create database) or attachment mutex in other entries
// 2. databases_mutex (when / if needed)
// 3. dbb_sync (when / if needed)
// 4. only for new attachments: attachment mutex when that attachment is created
@ -571,19 +586,20 @@ namespace
static const unsigned ATT_LOCK_ASYNC = 1;
AttachmentHolder(thread_db* tdbb, JAttachment* ja, unsigned lockAsync, const char* from)
: mutex(ja->getMutex(lockAsync & ATT_LOCK_ASYNC)),
attachment(NULL),
async(lockAsync)
: jAtt(ja),
async(lockAsync & ATT_LOCK_ASYNC)
{
mutex->enter(from);
jAtt->getMutex(async)->enter(from);
Jrd::Attachment* attachment = jAtt->getHandle(); // Must be done after entering mutex
try
{
attachment = ja->getHandle(); // Must be done after entering mutex
if (!attachment || engineShuttingDown)
if (!attachment || engineShutdown)
{
// this shutdown check is an optimization
// threads can enter engine with the flag set
// This shutdown check is an optimization, threads can still enter engine
// with the flag set cause shutdownMutex mutex is not locked here.
// That's not a danger cause check of att_use_count
// in shutdown code makes it anyway safe.
status_exception::raise(Arg::Gds(isc_att_shutdown));
}
@ -596,23 +612,23 @@ namespace
}
catch (const Firebird::Exception&)
{
mutex->leave();
jAtt->getMutex(async)->leave();
throw;
}
}
~AttachmentHolder()
{
if (!async)
Jrd::Attachment* attachment = jAtt->getHandle();
if (attachment && !async)
{
attachment->att_use_count--;
}
mutex->leave();
jAtt->getMutex(async)->leave();
}
private:
Firebird::Mutex* mutex;
Jrd::Attachment* attachment;
RefPtr<JAttachment> jAtt;
bool async;
private:
@ -929,7 +945,7 @@ static void release_attachment(thread_db*, Jrd::Attachment*);
static void rollback(thread_db*, jrd_tra*, const bool);
static bool shutdown_database(Database*, const bool);
static void strip_quotes(string&);
static void purge_attachment(thread_db*, Jrd::Attachment*, const bool);
static void purge_attachment(thread_db*, JAttachment*, const bool);
static void getUserInfo(UserId&, const DatabaseOptions&, const RefPtr<Config>*);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
@ -1215,7 +1231,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
bool invalid_client_SQL_dialect = false;
PathName org_filename, expanded_name;
bool is_alias = false;
MutexEnsureUnlock guardDbInit(db_init_mutex, FB_FUNCTION);
MutexEnsureUnlock guardDbInit(dbInitMutex, FB_FUNCTION);
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -1311,7 +1327,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
if (!(dbb->dbb_flags & DBB_new))
{
// That's already initialized DBB
// No need keeping db_init_mutex any more
// No need keeping dbInitMutex any more
guardDbInit.leave();
}
@ -1363,6 +1379,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
// Initialize locks
init_database_locks(tdbb);
jAtt->manualAsyncUnlock(attachment->att_flags);
INI_init(tdbb);
SHUT_init(tdbb);
@ -1400,7 +1417,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
SDW_init(tdbb, options.dpb_activate_shadow, options.dpb_delete_shadow);
CCH_init2(tdbb);
// Init complete - we can release db_init_mutex
// Init complete - we can release dbInitMutex
dbb->dbb_flags &= ~DBB_new;
guardDbInit.leave();
}
@ -1415,6 +1432,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
fb_assert(dbb->dbb_lock_mgr);
LCK_init(tdbb, LCK_OWNER_attachment);
jAtt->manualAsyncUnlock(attachment->att_flags);
INI_init(tdbb);
INI_init2(tdbb);
@ -2287,7 +2305,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char*
try
{
ThreadContextHolder tdbb(user_status);
MutexEnsureUnlock guardDbInit(db_init_mutex, FB_FUNCTION);
MutexEnsureUnlock guardDbInit(dbInitMutex, FB_FUNCTION);
UserId userId;
DatabaseOptions options;
@ -2512,6 +2530,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char*
// Initialize locks
init_database_locks(tdbb);
jAtt->manualAsyncUnlock(attachment->att_flags);
INI_init(tdbb);
PAG_init(tdbb);
@ -2605,7 +2624,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char*
dbb->dbb_backup_manager->dbCreating = false;
// Init complete - we can release db_init_mutex
// Init complete - we can release dbInitMutex
dbb->dbb_flags &= ~DBB_new;
guardDbInit.leave();
@ -2727,7 +2746,7 @@ void JAttachment::freeEngineData(IStatus* user_status)
**************************************/
try
{
EngineContextHolder tdbb(user_status, this, FB_FUNCTION, AttachmentHolder::ATT_LOCK_ASYNC);
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
try
{
@ -2736,7 +2755,7 @@ void JAttachment::freeEngineData(IStatus* user_status)
status_exception::raise(Arg::Gds(isc_attachment_in_use));
attachment->signalShutdown(tdbb);
purge_attachment(tdbb, attachment, false);
purge_attachment(tdbb, this, false);
att = NULL;
}
@ -2782,7 +2801,12 @@ void JAttachment::dropDatabase(IStatus* user_status)
status_exception::raise(Arg::Gds(isc_attachment_in_use));
}
{ // scope
// Prepare to set ODS to 0
WIN window(HEADER_PAGE_NUMBER);
Ods::header_page* header = NULL;
try
{
Sync sync(&dbb->dbb_sync, "JAttachment::dropDatabase()");
if (attachment->att_in_use || attachment->att_use_count)
@ -2815,6 +2839,9 @@ void JAttachment::dropDatabase(IStatus* user_status)
Arg::Gds(isc_obj_in_use) << Arg::Str(file_name));
}
// Lock header page before taking database lock
header = (Ods::header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
// Check if same process has more attachments
sync.lock(SYNC_EXCLUSIVE);
if (dbb->dbb_attachments && dbb->dbb_attachments->att_next)
@ -2835,11 +2862,9 @@ void JAttachment::dropDatabase(IStatus* user_status)
// Just mark the header page with an 0 ods version so that no other
// process can attach to this database once we release our exclusive
// lock and start dropping files.
WIN window(HEADER_PAGE_NUMBER);
Ods::header_page* header = (Ods::header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
header->hdr_ods_version = 0;
header = NULL; // In case of exception in CCH_RELEASE() do not repeat it in catch
CCH_RELEASE(tdbb, &window);
// Notify Trace API manager about successful drop of database
@ -2849,10 +2874,20 @@ void JAttachment::dropDatabase(IStatus* user_status)
attachment->att_trace_manager->event_detach(&conn, true);
}
}
catch (const Exception&)
{
if (header)
{
CCH_RELEASE(tdbb, &window);
}
CCH_release_exclusive(tdbb);
throw;
}
// Unlink attachment from database
release_attachment(tdbb, attachment);
att = NULL;
attachment = NULL;
guard.leave();
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
@ -3883,33 +3918,6 @@ void JRequest::start(IStatus* user_status, ITransaction* tra, int level)
}
class CounterGuard
{
public:
explicit CounterGuard(AtomicCounter& pc)
: counter(&pc)
{
for(;;)
{
if (counter->exchangeAdd(1) == 0)
{
break;
}
counter->exchangeAdd(-1);
THD_sleep(1);
}
}
~CounterGuard()
{
counter->exchangeAdd(-1);
}
private:
AtomicCounter* counter;
};
void JProvider::shutdown(IStatus* status, unsigned int timeout, const int reason)
{
/**************************************
@ -3924,18 +3932,18 @@ void JProvider::shutdown(IStatus* status, unsigned int timeout, const int reason
* database.
*
**************************************/
static AtomicCounter shutCounter;
static bool shutdownComplete = false;
try
{
CounterGuard guard(shutCounter);
MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
if (shutdownComplete)
if (engineShutdown)
{
return;
}
shutdownComplete = true;
{ // scope
MutexLockGuard guard(newAttachmentMutex, FB_FUNCTION);
engineShutdown = true;
}
ThreadContextHolder tdbb;
@ -5841,11 +5849,17 @@ static JAttachment* init(thread_db* tdbb,
*
* Functional description
* Initialize for database access. First call from both CREATE and ATTACH.
* Upon entry mutex db_init_mutex must be locked.
* Upon entry mutex dbInitMutex must be locked.
*
**************************************/
SET_TDBB(tdbb);
db_init_mutex->assertLocked();
fb_assert(dbInitMutex->locked());
// make sure that no new attachments arrive after shutdown started
if (engineShutdown)
{
Arg::Gds(isc_att_shutdown).raise();
}
// Initialize standard random generator.
// MSVC (at least since version 7) have per-thread random seed.
@ -5867,12 +5881,6 @@ static JAttachment* init(thread_db* tdbb,
{ // scope
MutexLockGuard listGuard(databases_mutex, FB_FUNCTION);
// make sure that no new attachments arrive after shutdown started
if (engineShuttingDown)
{
Arg::Gds(isc_att_shutdown).raise();
}
if (config->getSharedCache())
{
if (config->getSharedDatabase())
@ -5893,6 +5901,7 @@ static JAttachment* init(thread_db* tdbb,
{ // scope
MutexUnlockGuard listUnlock(databases_mutex, FB_FUNCTION);
fb_assert(!databases_mutex->locked());
// after unlocking databases_mutex we lose control over dbb
// as long as dbb_init_fini is not locked and its activity is not checked
@ -6010,6 +6019,14 @@ static JAttachment* create_attachment(const PathName& alias_name,
**************************************/
fb_assert(dbb->locked());
{ // scope
MutexLockGuard guard(newAttachmentMutex, FB_FUNCTION);
if (engineShutdown)
{
status_exception::raise(Arg::Gds(isc_att_shutdown));
}
}
Jrd::Attachment* attachment = Jrd::Attachment::create(dbb);
attachment->att_next = dbb->dbb_attachments;
dbb->dbb_attachments = attachment;
@ -6026,7 +6043,7 @@ static JAttachment* create_attachment(const PathName& alias_name,
attachment->att_ext_call_depth = options.dpb_ext_call_depth;
JAttachment* jAtt = new JAttachment(attachment);
jAtt->addRef(); // See also release() in unwindAttachment()
jAtt->addRef(); // See also REF_NO_INCR RefPtr in unwindAttach()
attachment->att_interface = jAtt;
jAtt->manualLock(attachment->att_flags);
@ -6649,7 +6666,7 @@ static unsigned int purge_transactions(thread_db* tdbb,
}
static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const bool force_flag)
static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, const bool force_flag)
{
/**************************************
*
@ -6664,19 +6681,58 @@ static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const
**************************************/
SET_TDBB(tdbb);
Mutex* attMutex = attachment->att_interface->getMutex();
fb_assert(!attMutex->locked());
Mutex* attMutex = jAtt->getMutex();
fb_assert(attMutex->locked());
MutexEnsureUnlock guard(*attMutex, FB_FUNCTION);
guard.enter();
Jrd::Attachment* attachment = NULL;
while (attachment->att_use_count)
while ((attachment = jAtt->getHandle()) && attachment->att_flags & ATT_purge_started)
{
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// !!!!!!!!!!!!!!!!! - event? semaphore? condvar? (when att_use_count == 0)
THD_yield();
THD_sleep(1);
attachment->att_use_count--;
{
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// !!!!!!!!!!!!!!!!! - event? semaphore? condvar? (when ATT_purge_started / jAtt->getHandle() changes)
fb_assert(!attMutex->locked());
THD_yield();
THD_sleep(1);
}
attachment = jAtt->getHandle();
if (attachment)
{
attachment->att_use_count++;
}
}
if (!attachment)
{
return;
}
attachment->att_flags |= ATT_purge_started;
fb_assert(attachment->att_use_count > 0);
attachment = jAtt->getHandle();
while (attachment && attachment->att_use_count > 1)
{
attachment->att_use_count--;
{
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// !!!!!!!!!!!!!!!!! - event? semaphore? condvar? (when --att_use_count)
fb_assert(!attMutex->locked());
THD_yield();
THD_sleep(1);
}
attachment = jAtt->getHandle();
if (attachment)
{
attachment->att_use_count++;
}
}
fb_assert(attMutex->locked());
if (!attachment)
return;
Database* const dbb = attachment->att_database;
@ -6734,6 +6790,7 @@ static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const
if (!force_flag)
{
attachment->att_flags |= (ATT_shutdown | ATT_purge_error);
attachment->att_flags &= ~ATT_purge_started;
throw;
}
}
@ -6762,6 +6819,7 @@ static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const
if (!force_flag)
{
attachment->att_flags |= (ATT_shutdown | ATT_purge_error);
attachment->att_flags &= ~ATT_purge_started;
throw;
}
}
@ -6773,10 +6831,26 @@ static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const
attachment->att_trace_manager->event_detach(&conn, false);
}
fb_assert(attMutex->locked());
Mutex* asyncMutex = jAtt->getMutex(true, true);
MutexEnsureUnlock asyncGuard(*asyncMutex, FB_FUNCTION);
{ // scope - ensure correct order of taking both async and main mutexes
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// !!!!!!!!!!!!!!!!! - event? semaphore? condvar? (when att_use_count == 0)
fb_assert(!attMutex->locked());
asyncGuard.enter();
}
if (!jAtt->getHandle())
return;
// Unlink attachment from database
release_attachment(tdbb, attachment);
guard.leave();
asyncGuard.leave();
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// If there are still attachments, do a partial shutdown
shutdown_database(dbb, true);
@ -7012,43 +7086,41 @@ static void unwindAttach(thread_db* tdbb, const Exception& ex, IStatus* userStat
{
transliterateException(tdbb, ex, userStatus, NULL);
if (dbb)
try
{
fb_assert(!dbb->locked());
{ // scope
MutexLockGuard guard(databases_mutex, FB_FUNCTION);
if (engineShuttingDown)
{
if (attachment && attachment->att_interface)
{
attachment->att_interface->manualUnlock(attachment->att_flags);
}
// this attachment will be released as part of engine shutdown process
// see also shutdown_thread
return;
}
}
try
if (dbb)
{
// A number of holders to make Attachment::destroy() happy
// See also addRef() in create_attachment()
RefPtr<JAttachment> jAtt(REF_NO_INCR, attachment->att_interface);
fb_assert(!dbb->locked());
ThreadStatusGuard temp_status(tdbb);
if (attachment)
{
release_attachment(tdbb, attachment);
// A number of holders to make Attachment::destroy() happy
// See also addRef() in create_attachment()
RefPtr<JAttachment> jAtt(REF_NO_INCR, attachment->att_interface);
// This unlocking/locking order guarantees stable release of attachment
jAtt->manualUnlock(attachment->att_flags);
ULONG flags = 0; // att_flags may already not exist here!
jAtt->manualLock(flags);
if (jAtt->getHandle())
{
attachment->att_flags |= flags;
release_attachment(tdbb, attachment);
}
else
{
jAtt->manualUnlock(flags);
}
}
shutdown_database(dbb, true);
}
catch (const Exception&)
{
// no-op
}
}
catch (const Exception&)
{
// no-op
}
return;
@ -7105,7 +7177,7 @@ namespace
{
JAttachment* jAtt = attachments[i];
MutexLockGuard guard(*(jAtt->getMutex(true)), FB_FUNCTION);
MutexLockGuard guard(*(jAtt->getMutex()), FB_FUNCTION);
Attachment* attachment = jAtt->getHandle();
if (attachment)
@ -7117,13 +7189,19 @@ namespace
try
{
// purge attachment, rollback any open transactions
purge_attachment(tdbb, attachment, true);
attachment->att_use_count++;
purge_attachment(tdbb, jAtt, true);
}
catch (const Exception& ex)
{
iscLogException("error while shutting down attachment", ex);
success = false;
}
attachment = jAtt->getHandle();
if (attachment)
{
attachment->att_use_count--;
}
}
}
@ -7134,6 +7212,13 @@ namespace
{
try
{
MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
if (engineShutdown)
{
// Shutdown was done, all attachmnets are gone
return 0;
}
shutdownAttachments(static_cast<AttQueue*>(arg));
}
catch(const Exception& ex)
@ -7174,9 +7259,6 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
{ // scope
MutexLockGuard guard(databases_mutex, FB_FUNCTION);
// First of all set flag to disable new attachments
engineShuttingDown = true;
for (Database* dbb = databases; dbb; dbb = dbb->dbb_next)
{
if ( !(dbb->dbb_flags & (DBB_bugcheck | DBB_security_db)) )

View File

@ -1155,6 +1155,11 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr
// Release the transaction and its pool
tdbb->setTransaction(NULL);
JTransaction* jTra = transaction->getInterface();
if (jTra)
{
jTra->setHandle(NULL);
}
jrd_tra::destroy(attachment, transaction);
}

View File

@ -526,7 +526,7 @@ TimerDelay curTime()
TimerEntry* getTimer(ITimer* timer)
{
timerAccess->assertLocked();
fb_assert(timerAccess->locked());
for (unsigned int i = 0; i < timerQueue->getCount(); ++i)
{