@@ -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 }
0 commit comments