Skip to content

Commit 57024ac

Browse files
author
Artyom Ivanov
committed
fix(shutdown): Reimplement synchronization between shutdown and init routine for GlobalObjectHolder
1 parent 18e55c4 commit 57024ac

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

src/common/classes/RefCounted.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ namespace Firebird
4848
return refCnt;
4949
}
5050

51+
AtomicCounter::counter_type getRefCount() const noexcept
52+
{
53+
return m_refCnt.value();
54+
}
55+
5156
void assertNonZero()
5257
{
5358
fb_assert(m_refCnt.value() > 0);

src/jrd/Database.cpp

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -586,21 +586,39 @@ namespace Jrd
586586
{
587587
MutexLockGuard guard(g_mutex, FB_FUNCTION);
588588

589-
while (g_shuttingDown)
589+
// Get entry with incrementing ref counter, so if someone is currently destroying it, the object itself
590+
// will remain alive.
591+
RefPtr<Database::GlobalObjectHolder::DbId> entry(g_hashTable->lookup(id));
592+
if (entry)
590593
{
591-
// Currently other GlobalObject is in shutdown process but he unlock the mutex for some reason,
592-
// so it's better to wait until this process is over.
593-
// This is done to eliminate potential race conditions involving global objects, such as shared memory.
594-
MutexUnlockGuard unlockGuard(g_mutex, FB_FUNCTION);
595-
Thread::yield();
596-
}
594+
auto& shutdownMutex = entry->shutdownMutex;
595+
// Check if someone else currently destroying GlobalObject.
596+
if (shutdownMutex.tryEnter(FB_FUNCTION))
597+
{
598+
// No one is destroying GlobalObject, continue init routine.
599+
shutdownMutex.leave();
600+
}
601+
else
602+
{
603+
// Someone is currently destroying GlobalObject, wait until he finish it to eliminate potential
604+
// race conditions.
605+
{
606+
MutexUnlockGuard unlockGuard(g_mutex, FB_FUNCTION);
597607

598-
Database::GlobalObjectHolder::DbId* entry = g_hashTable->lookup(id);
608+
MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
609+
}
610+
// Now we are the one who owned DbId object.
611+
// It also was removed from hash table, so simply delete it and recreate it next.
612+
fb_assert(entry->getRefCount() == 1);
613+
entry = nullptr;
614+
}
615+
}
599616
if (!entry)
600617
{
601618
const auto holder = FB_NEW Database::GlobalObjectHolder(id, filename, config);
602-
entry = FB_NEW Database::GlobalObjectHolder::DbId(id, holder);
619+
entry = makeRef(FB_NEW Database::GlobalObjectHolder::DbId(id, holder));
603620
g_hashTable->add(entry);
621+
entry->addRef();
604622
}
605623

606624
entry->holder->addRef();
@@ -610,16 +628,17 @@ namespace Jrd
610628
Database::GlobalObjectHolder::~GlobalObjectHolder()
611629
{
612630
// dtor is executed under g_mutex protection
613-
Database::GlobalObjectHolder::DbId* entry = g_hashTable->lookup(m_id);
614-
if (!g_hashTable->remove(m_id))
615-
fb_assert(false);
616631

617-
{ // scope
618-
// We need to unlock mutex to safely shutdown some managers, so set shutdown flag to make sure that
619-
// other threads will wait until we done our shutdown routine.
620-
// This is done to eliminate potential race conditions involving global objects, such as shared memory.
621-
AutoSetRestore shuttingDownGuard(&g_shuttingDown, true);
632+
// Stole the object from the hash table without incrementing ref counter, so we will be the one who will delete the object
633+
// at the end of this function.
634+
RefPtr<Database::GlobalObjectHolder::DbId> entry(REF_NO_INCR, g_hashTable->lookup(m_id));
635+
fb_assert(entry);
636+
// We need to unlock the global mutex to safely shutdown some managers, so lock shutdown mutex to make sure that
637+
// other threads will wait until we done our shutdown routine.
638+
// This is done to eliminate potential race conditions involving global objects, such as shared memory.
639+
MutexLockGuard guard(entry->shutdownMutex, FB_FUNCTION);
622640

641+
{ // scope
623642
// here we cleanup what should not be globally protected
624643
MutexUnlockGuard guard(g_mutex, FB_FUNCTION);
625644
if (m_replMgr)
@@ -630,7 +649,8 @@ namespace Jrd
630649
m_eventMgr = nullptr;
631650
m_replMgr = nullptr;
632651

633-
delete entry;
652+
if (!g_hashTable->remove(m_id))
653+
fb_assert(false);
634654

635655
fb_assert(m_tempCacheUsage == 0);
636656
}

src/jrd/Database.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ class Database : public pool_alloc<type_dbb>
256256
typedef Firebird::HashTable<DbId, Firebird::DEFAULT_HASH_SIZE,
257257
Firebird::string, DbId, DbId > DbIdHash;
258258

259-
struct DbId : public DbIdHash::Entry, public Firebird::GlobalStorage
259+
struct DbId : public DbIdHash::Entry, public Firebird::GlobalStorage, public Firebird::RefCounted
260260
{
261261
DbId(const Firebird::string& x, GlobalObjectHolder* h)
262262
: id(getPool(), x), holder(h)
@@ -286,6 +286,8 @@ class Database : public pool_alloc<type_dbb>
286286

287287
const Firebird::string id;
288288
GlobalObjectHolder* const holder;
289+
290+
Firebird::Mutex shutdownMutex;
289291
};
290292

291293
static Firebird::GlobalPtr<DbIdHash> g_hashTable;

0 commit comments

Comments
 (0)