8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 09:20:39 +01:00

Fixed bug #8151 : Deadlock happens when run 'List Trace Sessions' service and there are many active trace sessions

This commit is contained in:
Vlad Khorsun 2024-06-05 21:41:54 +03:00
parent 96c341288a
commit 9de13c6d3c
4 changed files with 85 additions and 21 deletions

View File

@ -90,8 +90,7 @@ ConfigStorage::ConfigStorage()
m_filename(getPool()),
m_recursive(0),
m_mutexTID(0),
m_dirty(false),
m_nextIdx(0)
m_dirty(false)
{
#ifdef WIN_NT
DWORD sesID = 0;
@ -744,19 +743,14 @@ bool ConfigStorage::getSession(Firebird::TraceSession& session, GET_FLAGS getFla
return readSession(slot, session, getFlag);
}
void ConfigStorage::restart()
{
m_nextIdx = 0;
}
bool ConfigStorage::getNextSession(TraceSession& session, GET_FLAGS getFlag)
bool ConfigStorage::getNextSession(TraceSession& session, GET_FLAGS getFlag, ULONG& nextIdx)
{
TraceCSHeader* header = m_sharedMemory->getHeader();
while (m_nextIdx < header->slots_cnt)
while (nextIdx < header->slots_cnt)
{
TraceCSHeader::Slot* slot = header->slots + m_nextIdx;
m_nextIdx++;
TraceCSHeader::Slot* slot = header->slots + nextIdx;
nextIdx++;
if (slot->used)
return readSession(slot, session, getFlag);
@ -895,6 +889,31 @@ void ConfigStorage::updateFlags(TraceSession& session)
slot->ses_flags = session.ses_flags;
}
bool ConfigStorage::Accessor::getNext(TraceSession& session, GET_FLAGS getFlag)
{
if (m_guard)
return m_storage->getNextSession(session, getFlag, m_nextIdx);
StorageGuard guard(m_storage);
// Restore position, if required: find index of slot with session ID greater than m_sesId.
if (m_change_number != m_storage->getChangeNumber())
{
if (m_storage->findSession(m_sesId, m_nextIdx))
m_nextIdx++;
m_change_number = m_storage->getChangeNumber();
}
if (m_storage->getNextSession(session, getFlag, m_nextIdx))
{
m_sesId = session.ses_id;
return true;
}
return false;
}
void ConfigStorage::Writer::write(ITEM tag, ULONG len, const void* data)
{
if (m_mem + 1 > m_end)

View File

@ -49,6 +49,8 @@ namespace Jrd {
Slot is reused with best-fit algorithm.
*/
class StorageGuard;
struct TraceCSHeader : public Firebird::MemoryHeader
{
static const USHORT TRACE_STORAGE_VERSION = 2;
@ -97,9 +99,6 @@ public:
// get session by sesion id
bool getSession(Firebird::TraceSession& session, GET_FLAGS getFlag);
void restart();
bool getNextSession(Firebird::TraceSession& session, GET_FLAGS getFlag);
ULONG getChangeNumber() const
{ return m_sharedMemory && m_sharedMemory->getHeader() ? m_sharedMemory->getHeader()->change_number : 0; }
@ -110,6 +109,35 @@ public:
Firebird::Mutex m_localMutex;
class Accessor
{
public:
// Use when storage is not locked by caller
explicit Accessor(ConfigStorage* storage) :
m_storage(storage),
m_guard(nullptr)
{}
// Use when storage is locked by caller
explicit Accessor(StorageGuard* guard);
void restart()
{
m_change_number = 0;
m_sesId = 0;
m_nextIdx = 0;
}
bool getNext(Firebird::TraceSession& session, GET_FLAGS getFlag);
private:
ConfigStorage* const m_storage;
StorageGuard* const m_guard;
ULONG m_change_number = 0;
ULONG m_sesId = 0; // last seen session ID
ULONG m_nextIdx = 0; // slot index next after last seen one
};
private:
void mutexBug(int osErrorCode, const char* text) override;
bool initialize(Firebird::SharedMemoryBase*, bool) override;
@ -181,6 +209,10 @@ private:
bool findSession(ULONG sesId, ULONG& idx);
bool readSession(TraceCSHeader::Slot* slot, Firebird::TraceSession& session, GET_FLAGS getFlag);
// Search for used slot starting from nextIdx and increments nextIdx to point to the next slot
// returns false, if used slot was not found
bool getNextSession(Firebird::TraceSession& session, GET_FLAGS getFlag, ULONG& nextIdx);
class Reader
{
public:
@ -217,7 +249,6 @@ private:
int m_recursive;
ThreadId m_mutexTID;
bool m_dirty;
ULONG m_nextIdx; // getNextSession() iterator index
};
@ -265,10 +296,22 @@ public:
{
m_storage->release();
}
ConfigStorage* getStorage()
{
return m_storage;
}
private:
ConfigStorage* m_storage;
};
inline ConfigStorage::Accessor::Accessor(StorageGuard* guard) :
m_storage(guard->getStorage()),
m_guard(guard)
{}
}
#endif

View File

@ -193,10 +193,10 @@ void TraceManager::update_sessions()
const bool noNewSessions = attachment && (attachment->att_purge_tid);
StorageGuard guard(storage);
storage->restart();
ConfigStorage::Accessor acc(&guard);
TraceSession session(pool);
while (storage->getNextSession(session, ConfigStorage::FLAGS))
while (acc.getNext(session, ConfigStorage::FLAGS))
{
if ((session.ses_flags & trs_active) && !(session.ses_flags & trs_log_full))
{

View File

@ -228,13 +228,15 @@ void TraceSvcJrd::listSessions()
{
m_svc.started();
ConfigStorage* storage = TraceManager::getStorage();
StorageGuard guard(storage);
// Writing into service when storage is locked could lead to deadlock,
// therefore don't use StorageGuard here.
storage->restart();
ConfigStorage* storage = TraceManager::getStorage();
ConfigStorage::Accessor acc(storage);
TraceSession session(*getDefaultMemoryPool());
while (storage->getNextSession(session, ConfigStorage::ALL))
while (acc.getNext(session, ConfigStorage::ALL))
{
if (checkPrivileges(session))
{