mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Squashed 'src/beast/' content from commit 43e6d34
git-subtree-dir: src/beast
git-subtree-split: 43e6d345e4
This commit is contained in:
561
modules/beast_core/thread/impl/TrackedMutex.cpp
Normal file
561
modules/beast_core/thread/impl/TrackedMutex.cpp
Normal file
@@ -0,0 +1,561 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// Example:
|
||||
//
|
||||
// m_mutex[2] @ beast_deadlineTimer.cpp(25)
|
||||
//
|
||||
String detail::TrackedMutexBasics::createName (String name,
|
||||
char const* fileName, int lineNumber, int instanceNumber)
|
||||
{
|
||||
return name +
|
||||
((instanceNumber > 1) ?
|
||||
(String ("[") + String::fromNumber (instanceNumber) + "] (") : " (") +
|
||||
Debug::getFileNameFromPath (fileName) + "," +
|
||||
String::fromNumber (lineNumber) + ")";
|
||||
}
|
||||
|
||||
Atomic <int> TrackedMutexBasics::lastThreadId;
|
||||
|
||||
ThreadLocalValue <TrackedMutexBasics::PerThreadDataStorage> TrackedMutexBasics::threadLocal;
|
||||
|
||||
TrackedMutexBasics::PerThreadData::PerThreadData ()
|
||||
: id (++lastThreadId)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutexBasics::PerThreadData& TrackedMutexBasics::getPerThreadData ()
|
||||
{
|
||||
PerThreadData& thread (*reinterpret_cast <PerThreadData*>(&threadLocal.get ()));
|
||||
|
||||
// Manually call the constructor with placement new if needed
|
||||
if (! thread.id)
|
||||
{
|
||||
::new (&thread) PerThreadData ();
|
||||
bassert (thread.id != 0);
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
CriticalSection& TrackedMutexBasics::getGlobalMutex ()
|
||||
{
|
||||
static CriticalSection mutex;
|
||||
return mutex;
|
||||
}
|
||||
|
||||
TrackedMutexBasics::Lists& TrackedMutexBasics::getLists ()
|
||||
{
|
||||
static Lists lists;
|
||||
return lists;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace detail
|
||||
|
||||
//==============================================================================
|
||||
|
||||
TrackedMutex::Record::Record (String mutexName,
|
||||
String threadName, String sourceLocation)
|
||||
: m_mutexName (mutexName)
|
||||
, m_threadName (threadName)
|
||||
, m_sourceLocation (sourceLocation)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Record::Record () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Record::Record (Record const& other) noexcept
|
||||
: m_mutexName (other.m_mutexName)
|
||||
, m_threadName (other.m_threadName)
|
||||
, m_sourceLocation (other.m_sourceLocation)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Record& TrackedMutex::Record::operator= (Record const& other) noexcept
|
||||
{
|
||||
m_mutexName = other.m_mutexName;
|
||||
m_threadName = other.m_threadName;
|
||||
m_sourceLocation = other.m_sourceLocation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool TrackedMutex::Record::isNull () const noexcept
|
||||
{
|
||||
return m_mutexName == "";
|
||||
}
|
||||
|
||||
bool TrackedMutex::Record::isNotNull () const noexcept
|
||||
{
|
||||
return m_mutexName != "";
|
||||
}
|
||||
|
||||
bool TrackedMutex::Record::asBoolean () const noexcept
|
||||
{
|
||||
return isNotNull ();
|
||||
}
|
||||
|
||||
String TrackedMutex::Record::getMutexName () const noexcept
|
||||
{
|
||||
return m_mutexName;
|
||||
}
|
||||
|
||||
String TrackedMutex::Record::getThreadName () const noexcept
|
||||
{
|
||||
return m_threadName;
|
||||
}
|
||||
|
||||
String TrackedMutex::Record::getSourceLocation () const noexcept
|
||||
{
|
||||
return m_sourceLocation;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TrackedMutex::Agent::Agent (detail::TrackedMutexBasics::PerThreadData* thread)
|
||||
: m_thread (thread)
|
||||
, m_threadName (thread->threadName)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Agent::Agent () noexcept
|
||||
: m_thread (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Agent::Agent (Agent const& other) noexcept
|
||||
: m_thread (other.m_thread)
|
||||
, m_threadName (other.m_threadName)
|
||||
, m_blocked (other.m_blocked)
|
||||
{
|
||||
}
|
||||
|
||||
TrackedMutex::Agent& TrackedMutex::Agent::operator= (Agent const& other) noexcept
|
||||
{
|
||||
m_thread = other.m_thread;
|
||||
m_threadName = other.m_threadName;
|
||||
m_blocked = other.m_blocked;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool TrackedMutex::Agent::isNull () const noexcept
|
||||
{
|
||||
return m_thread == nullptr;
|
||||
}
|
||||
|
||||
bool TrackedMutex::Agent::isNotNull () const noexcept
|
||||
{
|
||||
return m_thread != nullptr;
|
||||
}
|
||||
|
||||
bool TrackedMutex::Agent::asBoolean () const noexcept
|
||||
{
|
||||
return isNotNull ();
|
||||
}
|
||||
|
||||
String TrackedMutex::Agent::getThreadName () const noexcept
|
||||
{
|
||||
return m_threadName;
|
||||
}
|
||||
|
||||
TrackedMutex::Record TrackedMutex::Agent::getBlockedRecord () const noexcept
|
||||
{
|
||||
return m_blocked;
|
||||
}
|
||||
|
||||
bool TrackedMutex::Agent::getLockedList (Array <Record>& output)
|
||||
{
|
||||
bassert (isNotNull ());
|
||||
|
||||
output.clearQuick ();
|
||||
|
||||
typedef detail::TrackedMutexBasics::ThreadLockList ListType;
|
||||
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_thread->mutex);
|
||||
|
||||
ListType const& list (m_thread->list);
|
||||
|
||||
output.ensureStorageAllocated (list.size ());
|
||||
|
||||
for (ListType::const_iterator iter = list.begin ();
|
||||
iter != list.end (); ++iter)
|
||||
{
|
||||
TrackedMutex const& mutex = *iter;
|
||||
{
|
||||
TrackedMutex::SharedState::ReadAccess state (mutex.m_state);
|
||||
output.add (state->owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.size () > 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TrackedMutex::State::State ()
|
||||
: thread (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TrackedMutex::TrackedMutex (String const& name) noexcept
|
||||
: m_name (name)
|
||||
, m_count (0)
|
||||
{
|
||||
}
|
||||
|
||||
String TrackedMutex::getName () const noexcept
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
TrackedMutex::Record TrackedMutex::getOwnerRecord () const noexcept
|
||||
{
|
||||
{
|
||||
SharedState::ReadAccess state (m_state);
|
||||
return state->owner;
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMutex::Agent TrackedMutex::getOwnerAgent () const noexcept
|
||||
{
|
||||
{
|
||||
SharedState::ReadAccess state (m_state);
|
||||
if (state->thread != nullptr)
|
||||
return Agent (state->thread);
|
||||
}
|
||||
|
||||
return Agent ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void TrackedMutex::generateGlobalBlockedReport (StringArray& report)
|
||||
{
|
||||
report.clear ();
|
||||
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (
|
||||
detail::TrackedMutexBasics::getGlobalMutex ());
|
||||
|
||||
typedef detail::TrackedMutexBasics::GlobalThreadList ListType;
|
||||
|
||||
ListType const& list (detail::TrackedMutexBasics::getLists ().allThreads);
|
||||
|
||||
for (ListType::const_iterator iter = list.begin (); iter != list.end (); ++iter)
|
||||
{
|
||||
detail::TrackedMutexBasics::PerThreadData const* thread (
|
||||
&(*iter));
|
||||
|
||||
typedef detail::TrackedMutexBasics::ThreadLockList LockedList;
|
||||
LockedList const& owned (thread->list);
|
||||
|
||||
if (thread->blocked)
|
||||
{
|
||||
String s;
|
||||
s << thread->threadName << " blocked on " <<
|
||||
thread->blocked->getName () << " at " <<
|
||||
thread->sourceLocation;
|
||||
if (owned.size () > 0)
|
||||
s << " and owns these locks:";
|
||||
report.add (s);
|
||||
}
|
||||
else if (owned.size () > 0)
|
||||
{
|
||||
String s;
|
||||
s << thread->threadName << " owns these locks:";
|
||||
report.add (s);
|
||||
}
|
||||
|
||||
if (owned.size () > 0)
|
||||
{
|
||||
for (LockedList::const_iterator iter = owned.begin (); iter != owned.end (); ++iter)
|
||||
{
|
||||
String s;
|
||||
TrackedMutex const& mutex (*iter);
|
||||
TrackedMutex::SharedState::ReadAccess state (mutex.m_state);
|
||||
s << " " << mutex.getName () <<
|
||||
" from " << state->owner.getSourceLocation ();
|
||||
report.add (s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Called before we attempt to acquire the mutex.
|
||||
//
|
||||
void TrackedMutex::block (char const* fileName, int lineNumber) const noexcept
|
||||
{
|
||||
// Get calling thread data.
|
||||
detail::TrackedMutexBasics::PerThreadData& thread
|
||||
(detail::TrackedMutexBasics::getPerThreadData ());
|
||||
|
||||
++thread.refCount;
|
||||
|
||||
String const sourceLocation (makeSourceLocation (fileName, lineNumber));
|
||||
|
||||
{
|
||||
// Take a global lock.
|
||||
CriticalSection::ScopedLockType globalLock (
|
||||
detail::TrackedMutexBasics::getGlobalMutex ());
|
||||
|
||||
{
|
||||
// Take a thread lock.
|
||||
CriticalSection::ScopedLockType threadLock (thread.mutex);
|
||||
|
||||
// Set the thread's blocked record
|
||||
thread.blocked = this;
|
||||
thread.threadName = makeThreadName (thread);
|
||||
thread.sourceLocation = sourceLocation;
|
||||
|
||||
// Add this thread to threads list.
|
||||
if (thread.refCount == 1)
|
||||
detail::TrackedMutexBasics::getLists().allThreads.push_back (thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Called after we already have ownership of the mutex
|
||||
//
|
||||
void TrackedMutex::acquired (char const* fileName, int lineNumber) const noexcept
|
||||
{
|
||||
// Retrieve the per-thread data for the calling thread.
|
||||
detail::TrackedMutexBasics::PerThreadData& thread
|
||||
(detail::TrackedMutexBasics::getPerThreadData ());
|
||||
|
||||
// If this goes off it means block() wasn't called.
|
||||
bassert (thread.refCount > 0);
|
||||
|
||||
++m_count;
|
||||
|
||||
// Take ownership on the first count.
|
||||
if (m_count == 1)
|
||||
{
|
||||
// Thread is a new owner of the mutex.
|
||||
String const sourceLocation (makeSourceLocation (fileName, lineNumber));
|
||||
String const threadName (makeThreadName (thread));
|
||||
|
||||
{
|
||||
// Take a global lock.
|
||||
CriticalSection::ScopedLockType globalLock (
|
||||
detail::TrackedMutexBasics::getGlobalMutex ());
|
||||
|
||||
{
|
||||
// Take a state lock.
|
||||
SharedState::WriteAccess state (m_state);
|
||||
|
||||
// Set the mutex ownership record
|
||||
state->owner = Record (getName (), threadName, sourceLocation);
|
||||
state->thread = &thread;
|
||||
|
||||
{
|
||||
// Take a thread lock.
|
||||
CriticalSection::ScopedLockType threadLock (thread.mutex);
|
||||
|
||||
// Add the mutex to the thread's list.
|
||||
thread.list.push_back (const_cast <TrackedMutex&>(*this));
|
||||
|
||||
// Unblock the thread record.
|
||||
thread.blocked = nullptr;
|
||||
thread.sourceLocation = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Thread already had ownership of the mutex.
|
||||
bassert (SharedState::UnlockedAccess (m_state)->thread == &thread);
|
||||
|
||||
// If this goes off it means we counted wrong.
|
||||
bassert (thread.refCount >= m_count);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void TrackedMutex::release () const noexcept
|
||||
{
|
||||
// If this goes off it means we don't own the mutex!
|
||||
bassert (m_count > 0);
|
||||
|
||||
// Retrieve the per-thread data for the calling thread.
|
||||
detail::TrackedMutexBasics::PerThreadData& thread
|
||||
(detail::TrackedMutexBasics::getPerThreadData ());
|
||||
|
||||
// If this goes off it means we counted wrong.
|
||||
bassert (thread.refCount >= m_count);
|
||||
|
||||
--m_count;
|
||||
--thread.refCount;
|
||||
|
||||
// Give up ownership when the count drops to zero.
|
||||
if (m_count == 0)
|
||||
{
|
||||
// Take the global mutex
|
||||
CriticalSection::ScopedLockType globalLock (
|
||||
detail::TrackedMutexBasics::getGlobalMutex ());
|
||||
|
||||
{
|
||||
// Take the mutex' state lock
|
||||
SharedState::WriteAccess state (m_state);
|
||||
|
||||
// Clear the mutex ownership record
|
||||
state->owner = Record ();
|
||||
state->thread = nullptr;
|
||||
|
||||
{
|
||||
// Take the thread mutex
|
||||
CriticalSection::ScopedLockType threadLock (thread.mutex);
|
||||
|
||||
// Remove this mutex from the list of the thread's owned locks.
|
||||
thread.list.erase (thread.list.iterator_to (
|
||||
const_cast <TrackedMutex&>(*this)));
|
||||
|
||||
// Remove this thread from the threads list.
|
||||
if (thread.refCount == 0)
|
||||
{
|
||||
typedef detail::TrackedMutexBasics::GlobalThreadList ListType;
|
||||
ListType& list (detail::TrackedMutexBasics::getLists().allThreads);
|
||||
list.erase (list.iterator_to (thread));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
String TrackedMutex::makeThreadName (
|
||||
detail::TrackedMutexBasics::PerThreadData const& thread) noexcept
|
||||
{
|
||||
String threadName;
|
||||
Thread const* const currentThread (Thread::getCurrentThread ());
|
||||
if (currentThread != nullptr)
|
||||
threadName = currentThread->getThreadName ();
|
||||
threadName = threadName + "[" + String::fromNumber (thread.id) + "]";
|
||||
return threadName;
|
||||
}
|
||||
|
||||
String TrackedMutex::makeSourceLocation (char const* fileName, int lineNumber) noexcept
|
||||
{
|
||||
String const sourceLocation (Debug::getFileNameFromPath (fileName, 1) + "(" +
|
||||
String::fromNumber (lineNumber) + ")");
|
||||
|
||||
return sourceLocation;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class TrackedMutexUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
typedef TrackedMutexType <CriticalSection> Mutex;
|
||||
|
||||
struct LockingThread : public Thread
|
||||
{
|
||||
Mutex& m_m1;
|
||||
Mutex& m_m2;
|
||||
WaitableEvent m_start;
|
||||
|
||||
explicit LockingThread (String name, Mutex& m1, Mutex& m2)
|
||||
: Thread (name)
|
||||
, m_m1 (m1)
|
||||
, m_m2 (m2)
|
||||
{
|
||||
startThread ();
|
||||
}
|
||||
|
||||
void waitForStart ()
|
||||
{
|
||||
m_start.wait ();
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
Mutex::ScopedLockType l2 (m_m2, __FILE__, __LINE__);
|
||||
{
|
||||
Mutex::ScopedLockType l1 (m_m1, __FILE__, __LINE__);
|
||||
m_start.signal ();
|
||||
{
|
||||
Mutex::ScopedUnlockType ul1 (m_m1, __FILE__, __LINE__);
|
||||
this->wait ();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void report (String name)
|
||||
{
|
||||
beginTestCase (name);
|
||||
StringArray report;
|
||||
TrackedMutex::generateGlobalBlockedReport (report);
|
||||
logReport (report);
|
||||
pass ();
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
Mutex m1 ("M1", __FILE__, __LINE__);
|
||||
Mutex m2 ("M2", __FILE__, __LINE__);
|
||||
|
||||
{
|
||||
Mutex::ScopedLockType l1 (m1, __FILE__, __LINE__);
|
||||
LockingThread t1 ("T1", m1, m2);
|
||||
{
|
||||
Mutex::ScopedUnlockType ul1 (m1, __FILE__, __LINE__);
|
||||
t1.waitForStart ();
|
||||
}
|
||||
report ("#1");
|
||||
{
|
||||
t1.notify ();
|
||||
Mutex::ScopedUnlockType ul1 (m1, __FILE__, __LINE__);
|
||||
t1.waitForThreadToExit ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMutexUnitTests () : UnitTest ("TrackedMutex", "beast", runManual)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static TrackedMutexUnitTests trackedMutexUnitTests;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
168
modules/beast_core/thread/impl/TrackedMutex.h
Normal file
168
modules/beast_core/thread/impl/TrackedMutex.h
Normal file
@@ -0,0 +1,168 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CORE_THREAD_IMPL_TRACKEDMUTEX_H_INCLUDED
|
||||
#define BEAST_CORE_THREAD_IMPL_TRACKEDMUTEX_H_INCLUDED
|
||||
|
||||
/** Common types and member functions for a TrackedMutex */
|
||||
class TrackedMutex
|
||||
: public detail::TrackedMutexBasics::ThreadLockList::Node
|
||||
{
|
||||
public:
|
||||
class Agent;
|
||||
class Location;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** A triplet identifying a mutex, a thread, and source code location.
|
||||
*/
|
||||
class Record : public SafeBool <Record>
|
||||
{
|
||||
public:
|
||||
Record () noexcept;
|
||||
Record (Record const& other) noexcept;
|
||||
Record& operator= (Record const& other) noexcept;
|
||||
|
||||
bool isNull () const noexcept;
|
||||
bool isNotNull () const noexcept;
|
||||
bool asBoolean () const noexcept;
|
||||
|
||||
/** Returns the name of the mutex.
|
||||
Since the Mutex may not exist after the Record record is
|
||||
created, we only provide a String, which is always valid.
|
||||
*/
|
||||
String getMutexName () const noexcept;
|
||||
|
||||
/** Returns the name of the associated thread.
|
||||
The name is generated at the time the record is created,
|
||||
and might have changed since that time, or may no longer exist.
|
||||
*/
|
||||
String getThreadName () const noexcept;
|
||||
|
||||
/** Returns the position within the source code.
|
||||
This will either be the place a lock was acquired, or the place
|
||||
where a thread is trying to acquire a lock. The vaue is only
|
||||
meaningful at the time the Record is created. Since then, the
|
||||
thread may have changed its state.
|
||||
*/
|
||||
String getSourceLocation () const noexcept;
|
||||
|
||||
private:
|
||||
friend class TrackedMutex;
|
||||
|
||||
Record (String mutexName, String threadName, String sourceLocation);
|
||||
|
||||
String m_mutexName;
|
||||
String m_threadName;
|
||||
String m_sourceLocation;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Describes a thread that can acquire mutexes. */
|
||||
class Agent : public SafeBool <Agent>
|
||||
{
|
||||
public:
|
||||
Agent () noexcept;
|
||||
Agent (Agent const& other) noexcept;
|
||||
Agent& operator= (Agent const& other) noexcept;
|
||||
|
||||
bool isNull () const noexcept;
|
||||
bool isNotNull () const noexcept;
|
||||
bool asBoolean () const noexcept;
|
||||
|
||||
/** Returns the name of the thread.
|
||||
The name is generated at the time the Agent record is created,
|
||||
and might have changed since that time.
|
||||
*/
|
||||
String getThreadName () const noexcept;
|
||||
|
||||
/** Returns a Record indicating where the thread is blocked on a mutex.
|
||||
If the thread is not blocked, a null Record is returned.
|
||||
The value is only meaningful at the moment of the call as conditions
|
||||
can change.
|
||||
*/
|
||||
Record getBlockedRecord () const noexcept;
|
||||
|
||||
/** Retrieve a list of other locks that this thread holds.
|
||||
Each lock is represented by a Location indicating the place
|
||||
The value is only meaningful at the moment of the call as conditions
|
||||
can change.
|
||||
@return `true` if the list is not empty.
|
||||
*/
|
||||
bool getLockedList (Array <Record>& list);
|
||||
|
||||
private:
|
||||
friend class TrackedMutex;
|
||||
explicit Agent (detail::TrackedMutexBasics::PerThreadData* thread);
|
||||
|
||||
detail::TrackedMutexBasics::PerThreadData* m_thread;
|
||||
String m_threadName;
|
||||
Record m_blocked;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Retrieve the name of this mutex.
|
||||
Thread safety: May be called from any thread.
|
||||
*/
|
||||
String getName () const noexcept;
|
||||
|
||||
/** Retrieve a Record for the current owner.
|
||||
It is only valid at the one instant in time, as the person holding it
|
||||
might have released it shortly afterwards. If there is no owner,
|
||||
a null Record is returned.
|
||||
*/
|
||||
Record getOwnerRecord () const noexcept;
|
||||
|
||||
/** Retrieve the Agent for the current owner.
|
||||
It is only valid at the one instant in time, as the person holding it
|
||||
might have released it shortly afterwards. If there is no owner,
|
||||
a null Agent is returned.
|
||||
*/
|
||||
Agent getOwnerAgent () const noexcept;
|
||||
|
||||
/** Produce a report on the state of all blocked threads. */
|
||||
static void generateGlobalBlockedReport (StringArray& report);
|
||||
|
||||
protected:
|
||||
static String makeThreadName (detail::TrackedMutexBasics::PerThreadData const&) noexcept;
|
||||
static String makeSourceLocation (char const* fileName, int lineNumber) noexcept;
|
||||
|
||||
TrackedMutex (String const& name) noexcept;
|
||||
void block (char const* fileName, int lineNumber) const noexcept;
|
||||
void acquired (char const* fileName, int lineNumber) const noexcept;
|
||||
void release () const noexcept;
|
||||
|
||||
private:
|
||||
struct State
|
||||
{
|
||||
State ();
|
||||
Record owner;
|
||||
detail::TrackedMutexBasics::PerThreadData* thread;
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
|
||||
String const m_name;
|
||||
int mutable m_count;
|
||||
SharedState mutable m_state;
|
||||
};
|
||||
|
||||
#endif
|
||||
95
modules/beast_core/thread/impl/TrackedMutexType.h
Normal file
95
modules/beast_core/thread/impl/TrackedMutexType.h
Normal file
@@ -0,0 +1,95 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CORE_THREAD_TRACKEDMUTEXTYPE_H_INCLUDED
|
||||
#define BEAST_CORE_THREAD_TRACKEDMUTEXTYPE_H_INCLUDED
|
||||
|
||||
/** A template that gives a Mutex diagnostic tracking capabilities. */
|
||||
template <typename Mutex>
|
||||
class TrackedMutexType
|
||||
: public TrackedMutex
|
||||
{
|
||||
public:
|
||||
/** The type of ScopedLock to use with this TrackedMutexType object. */
|
||||
typedef detail::TrackedScopedLock <TrackedMutexType <Mutex> > ScopedLockType;
|
||||
|
||||
/** The type of ScopedTrylock to use with this TrackedMutexType object. */
|
||||
typedef detail::TrackedScopedTryLock <TrackedMutexType <Mutex> > ScopedTryLockType;
|
||||
|
||||
/** The type of ScopedUnlock to use with this TrackedMutexType object. */
|
||||
typedef detail::TrackedScopedUnlock <TrackedMutexType <Mutex> > ScopedUnlockType;
|
||||
|
||||
/** Construct a mutex, keyed to a particular class.
|
||||
Just pass 'this' for owner and give it the name of the data member
|
||||
of your class.
|
||||
*/
|
||||
template <typename Object>
|
||||
TrackedMutexType (Object const* object,
|
||||
String name,
|
||||
char const* fileName,
|
||||
int lineNumber)
|
||||
: TrackedMutex (detail::TrackedMutexBasics::createName <Object> (
|
||||
name, fileName, lineNumber))
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a mutex, without a class association.
|
||||
These will all get numbered together as a group.
|
||||
*/
|
||||
TrackedMutexType (String name, char const* fileName, int lineNumber)
|
||||
: TrackedMutex (detail::TrackedMutexBasics::createName (name,
|
||||
fileName, lineNumber))
|
||||
{
|
||||
}
|
||||
|
||||
~TrackedMutexType () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline void lock (char const* fileName, int lineNumber) const noexcept
|
||||
{
|
||||
block (fileName, lineNumber);
|
||||
MutexTraits <Mutex>::lock (m_mutex);
|
||||
acquired (fileName, lineNumber);
|
||||
}
|
||||
|
||||
inline void unlock () const noexcept
|
||||
{
|
||||
release ();
|
||||
MutexTraits <Mutex>::unlock (m_mutex);
|
||||
}
|
||||
|
||||
// VFALCO NOTE: We could use enable_if here...
|
||||
inline bool try_lock (char const* fileName, int lineNumber) const noexcept
|
||||
{
|
||||
bool const success = MutexTraits <Mutex>::try_lock (m_mutex);
|
||||
if (success)
|
||||
{
|
||||
// Hack, call block to prevent counts from going wrong.
|
||||
block (fileName, lineNumber);
|
||||
acquired (fileName, lineNumber);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex const m_mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
82
modules/beast_core/thread/impl/UntrackedMutexType.h
Normal file
82
modules/beast_core/thread/impl/UntrackedMutexType.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CORE_THREAD_IMPL_UNTRACKEDMUTEX_H_INCLUDED
|
||||
#define BEAST_CORE_THREAD_IMPL_UNTRACKEDMUTEX_H_INCLUDED
|
||||
|
||||
/** A drop-in replacement for TrackedMutex without the tracking.
|
||||
*/
|
||||
template <typename Mutex>
|
||||
class UntrackedMutexType
|
||||
: public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef detail::UntrackedScopedLock <UntrackedMutexType <Mutex> > ScopedLockType;
|
||||
typedef detail::UntrackedScopedTryLock <UntrackedMutexType <Mutex> > ScopedTryLockType;
|
||||
typedef detail::UntrackedScopedUnlock <UntrackedMutexType <Mutex> > ScopedUnlockType;
|
||||
|
||||
template <typename Object>
|
||||
inline UntrackedMutexType (Object const*, String name, char const*, int) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline UntrackedMutexType (String, char const*, int) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline UntrackedMutexType () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline ~UntrackedMutexType () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline void lock () const noexcept
|
||||
{
|
||||
MutexTraits <Mutex>::lock (m_mutex);
|
||||
}
|
||||
|
||||
inline void lock (char const*, int) const noexcept
|
||||
{
|
||||
MutexTraits <Mutex>::lock (m_mutex);
|
||||
}
|
||||
|
||||
inline void unlock () const noexcept
|
||||
{
|
||||
MutexTraits <Mutex>::unlock (m_mutex);
|
||||
}
|
||||
|
||||
// VFALCO NOTE: We could use enable_if here...
|
||||
|
||||
inline bool try_lock () const noexcept
|
||||
{
|
||||
return MutexTraits <Mutex>::try_lock (m_mutex);
|
||||
}
|
||||
|
||||
inline bool try_lock (char const*, int) const noexcept
|
||||
{
|
||||
return MutexTraits <Mutex>::try_lock (m_mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mutable m_mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user