Skip to content

Commit

Permalink
Fixed bug #8151 : Deadlock happens when run 'List Trace Sessions' ser…
Browse files Browse the repository at this point in the history
…vice and there are many active trace sessions
  • Loading branch information
hvlad committed Jun 5, 2024
1 parent 07d09c3 commit fb83321
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 23 deletions.
8 changes: 6 additions & 2 deletions src/jrd/svc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1958,6 +1958,8 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
Arg::Gds(isc_bad_svc_handle).raise();
}

bool needTraceEvent = false;

try
{
if (!svcShutdown)
Expand Down Expand Up @@ -2096,6 +2098,8 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
svc_status->init();
}

needTraceEvent = svc_trace_manager->needs(ITraceFactory::TRACE_EVENT_SERVICE_START);

if (serv->serv_thd)
{
svc_flags &= ~(SVC_evnt_fired | SVC_finished);
Expand Down Expand Up @@ -2134,7 +2138,7 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
} // try
catch (const Firebird::Exception& ex)
{
if (svc_trace_manager->needs(ITraceFactory::TRACE_EVENT_SERVICE_START))
if (needTraceEvent)
{
FbLocalStatus status_vector;
ex.stuffException(&status_vector);
Expand All @@ -2150,7 +2154,7 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
throw;
}

if (this->svc_trace_manager->needs(ITraceFactory::TRACE_EVENT_SERVICE_START))
if (needTraceEvent)
{
TraceServiceImpl service(this);
this->svc_trace_manager->event_service_start(&service,
Expand Down
41 changes: 30 additions & 11 deletions src/jrd/trace/TraceConfigStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 47 additions & 4 deletions src/jrd/trace/TraceConfigStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -97,9 +99,6 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc
// 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; }

Expand All @@ -110,6 +109,35 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc

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;
Expand Down Expand Up @@ -181,6 +209,10 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc
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:
Expand Down Expand Up @@ -217,7 +249,6 @@ class ConfigStorage final : public Firebird::GlobalStorage, public Firebird::Ipc
int m_recursive;
ThreadId m_mutexTID;
bool m_dirty;
ULONG m_nextIdx; // getNextSession() iterator index
};


Expand Down Expand Up @@ -265,10 +296,22 @@ class StorageGuard : public Firebird::MutexLockGuard
{
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
4 changes: 2 additions & 2 deletions src/jrd/trace/TraceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down
10 changes: 6 additions & 4 deletions src/jrd/trace/TraceService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,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))
{
Expand Down

0 comments on commit fb83321

Please sign in to comment.