Files
itgmania212121/stepmania/src/RageThreads.cpp
T

734 lines
18 KiB
C++
Raw Normal View History

/*
* If you're going to use threads, remember this:
*
* Threads suck.
*
* If there's any way to avoid them, take it! Threaded code an order of
* magnitude more complicated, harder to debug and harder to make robust.
*
2004-06-14 05:46:49 +00:00
* That said, here are a few helpers for when they're unavoidable.
*/
2003-02-16 04:01:45 +00:00
#include "global.h"
#include "RageThreads.h"
2002-12-21 05:13:45 +00:00
#include "RageTimer.h"
#include "RageLog.h"
#include "RageUtil.h"
2003-07-27 03:08:02 +00:00
2004-04-05 05:22:32 +00:00
#include <cerrno>
2004-03-21 08:36:43 +00:00
#include <set>
2003-07-27 03:08:02 +00:00
2004-06-11 19:24:05 +00:00
#include "arch/Threads/Threads.h"
#include "arch/Dialog/Dialog.h"
2004-03-20 22:01:40 +00:00
2004-06-14 11:01:10 +00:00
#if defined(CRASH_HANDLER)
2004-06-15 06:24:58 +00:00
#if defined(_WINDOWS)
#include "archutils/Win32/crash.h"
2004-06-14 11:01:10 +00:00
#elif defined(LINUX) || defined(DARWIN)
2004-06-11 19:24:05 +00:00
#include "archutils/Unix/CrashHandler.h"
2004-03-28 06:04:37 +00:00
#endif
2004-06-11 19:29:03 +00:00
#endif
2004-03-28 06:04:37 +00:00
2004-09-09 02:28:05 +00:00
/* Assume TLS doesn't work until told otherwise. It's ArchHooks's job to set this. */
bool RageThread::m_bSystemSupportsTLS = false;
2003-07-27 03:08:02 +00:00
#define MAX_THREADS 128
//static vector<RageMutex*> *g_MutexList = NULL; /* watch out for static initialization order problems */
2003-07-27 03:08:02 +00:00
struct ThreadSlot
{
2004-03-20 22:01:40 +00:00
mutable char name[1024]; /* mutable so we can force nul-termination */
2003-07-27 03:08:02 +00:00
/* Format this beforehand, since it's easier to do that than to do it under crash conditions. */
char ThreadFormattedOutput[1024];
2003-07-27 03:08:02 +00:00
bool used;
2004-06-13 07:55:27 +00:00
uint64_t id;
2003-07-27 03:08:02 +00:00
2004-06-11 19:24:05 +00:00
ThreadImpl *pImpl;
2004-03-28 06:04:37 +00:00
#undef CHECKPOINT_COUNT
2003-08-08 09:03:30 +00:00
#define CHECKPOINT_COUNT 5
struct ThreadCheckpoint
{
const char *File, *Message;
int Line;
char FormattedBuf[1024];
ThreadCheckpoint() { Set( NULL, 0, NULL ); }
void Set(const char *File_, int Line_, const char *Message_=NULL);
const char *GetFormattedCheckpoint();
};
ThreadCheckpoint Checkpoints[CHECKPOINT_COUNT];
int CurCheckpoint, NumCheckpoints;
const char *GetFormattedCheckpoint( int lineno );
ThreadSlot() { Init(); }
void Init()
2003-07-27 03:08:02 +00:00
{
2004-06-13 07:55:27 +00:00
id = GetInvalidThreadId();
CurCheckpoint = NumCheckpoints = 0;
2004-06-11 19:24:05 +00:00
pImpl = NULL;
2004-06-13 07:55:27 +00:00
/* Reset used last; otherwise, a thread creation might pick up the slot. */
used = false;
2003-07-27 03:08:02 +00:00
}
2004-03-20 22:01:40 +00:00
const char *GetThreadName() const;
2003-07-27 03:08:02 +00:00
};
2004-06-11 19:24:05 +00:00
void ThreadSlot::ThreadCheckpoint::Set(const char *File_, int Line_, const char *Message_)
{
File=File_;
Line=Line_;
Message=Message_;
sprintf( FormattedBuf, " %s:%i %s",
File, Line, Message? Message:"" );
}
const char *ThreadSlot::ThreadCheckpoint::GetFormattedCheckpoint()
{
if( File == NULL )
return NULL;
/* Make sure it's terminated: */
FormattedBuf [ sizeof(FormattedBuf)-1 ] = 0;
return FormattedBuf;
}
const char *ThreadSlot::GetFormattedCheckpoint( int lineno )
{
if( lineno >= CHECKPOINT_COUNT || lineno >= NumCheckpoints )
return NULL;
if( NumCheckpoints == CHECKPOINT_COUNT )
{
lineno += CurCheckpoint;
lineno %= CHECKPOINT_COUNT;
}
return Checkpoints[lineno].GetFormattedCheckpoint();
}
2003-07-27 03:08:02 +00:00
static ThreadSlot g_ThreadSlots[MAX_THREADS];
2004-06-11 19:24:05 +00:00
struct ThreadSlot *g_pUnknownThreadSlot = NULL;
2004-06-13 07:09:41 +00:00
2004-06-13 07:55:27 +00:00
/* Lock this mutex before using or modifying pImpl. Other values are just identifiers,
* so possibly racing over them is harmless (simply using a stale thread ID, etc). */
static RageMutex g_ThreadSlotsLock("ThreadSlots");
2003-07-27 03:08:02 +00:00
static int FindEmptyThreadSlot()
{
LockMut(g_ThreadSlotsLock);
for( int entry = 0; entry < MAX_THREADS; ++entry )
{
if( g_ThreadSlots[entry].used )
continue;
2004-06-16 01:53:27 +00:00
g_ThreadSlots[entry].used = true;
2003-07-27 03:08:02 +00:00
return entry;
}
RageException::Throw("Out of thread slots!");
}
2004-06-16 01:14:43 +00:00
static void InitThreads()
{
/* We don't have to worry about two threads calling this at on */
static bool bInitialized = false;
if( bInitialized )
return;
g_ThreadSlotsLock.Lock();
/* Libraries might start threads on their own, which might call user callbacks,
* which could come back here. Make sure we don't accidentally initialize twice. */
if( bInitialized )
{
g_ThreadSlotsLock.Unlock();
return;
}
bInitialized = true;
/* Register the "unknown thread" slot. */
int slot = FindEmptyThreadSlot();
strcpy( g_ThreadSlots[slot].name, "Unknown thread" );
g_ThreadSlots[slot].id = GetInvalidThreadId();
sprintf( g_ThreadSlots[slot].ThreadFormattedOutput, "Unknown thread" );
g_pUnknownThreadSlot = &g_ThreadSlots[slot];
g_ThreadSlotsLock.Unlock();
}
2004-06-11 19:24:05 +00:00
static ThreadSlot *GetThreadSlotFromID( uint64_t iID )
2003-07-27 03:08:02 +00:00
{
2004-06-16 01:14:43 +00:00
InitThreads();
2003-07-27 03:08:02 +00:00
for( int entry = 0; entry < MAX_THREADS; ++entry )
{
if( !g_ThreadSlots[entry].used )
continue;
2004-06-13 07:55:27 +00:00
if( g_ThreadSlots[entry].id == iID )
2004-06-11 19:24:05 +00:00
return &g_ThreadSlots[entry];
2003-07-27 03:08:02 +00:00
}
2004-06-11 19:24:05 +00:00
return NULL;
2003-07-27 03:08:02 +00:00
}
2004-06-11 19:24:05 +00:00
static ThreadSlot *GetCurThreadSlot()
2004-04-20 03:44:38 +00:00
{
return GetThreadSlotFromID( RageThread::GetCurrentThreadID() );
}
2004-06-11 19:24:05 +00:00
static ThreadSlot *GetUnknownThreadSlot()
2004-02-20 23:42:40 +00:00
{
2004-06-11 19:24:05 +00:00
return g_pUnknownThreadSlot;
2004-02-20 23:42:40 +00:00
}
2003-07-27 03:08:02 +00:00
RageThread::RageThread()
{
2004-06-11 19:24:05 +00:00
m_pSlot = NULL;
2003-07-27 03:08:02 +00:00
}
RageThread::~RageThread()
{
}
2004-03-20 22:01:40 +00:00
const char *ThreadSlot::GetThreadName() const
{
/* This function may be called in crash conditions, so guarantee the string
* is null-terminated. */
name[ sizeof(name)-1] = 0;
return name;
}
2003-07-27 03:08:02 +00:00
void RageThread::Create( int (*fn)(void *), void *data )
{
/* Don't create a thread that's already running: */
2004-06-11 19:24:05 +00:00
ASSERT( m_pSlot == NULL );
2003-07-27 03:08:02 +00:00
2004-06-16 01:14:43 +00:00
InitThreads();
2004-06-13 07:09:41 +00:00
/* Lock unused slots, so nothing else uses our slot before we mark it used. */
LockMut(g_ThreadSlotsLock);
2003-07-27 03:08:02 +00:00
int slotno = FindEmptyThreadSlot();
2004-06-11 19:24:05 +00:00
m_pSlot = &g_ThreadSlots[slotno];
2003-07-27 03:08:02 +00:00
if( name == "" )
{
2004-08-14 23:28:33 +00:00
if( LOG )
LOG->Warn("Created a thread without naming it first.");
2003-07-27 03:08:02 +00:00
/* If you don't name it, I will: */
2004-06-11 19:24:05 +00:00
strcpy( m_pSlot->name, "Joe" );
2003-07-27 03:08:02 +00:00
} else {
2004-06-11 19:24:05 +00:00
strcpy( m_pSlot->name, name.c_str() );
2003-07-27 03:08:02 +00:00
}
2004-08-14 23:28:33 +00:00
if( LOG )
LOG->Trace( "Starting thread: %s", name.c_str() );
2004-06-11 19:24:05 +00:00
sprintf( m_pSlot->ThreadFormattedOutput, "Thread: %s", name.c_str() );
2004-06-16 01:53:27 +00:00
/* Start a thread using our own startup function. We pass the id to fill in,
* to make sure it's set before the thread actually starts. (Otherwise, early
* checkpoints might not have a completely set-up thread slot.) */
m_pSlot->pImpl = MakeThread( fn, data, &m_pSlot->id );
2003-07-27 03:08:02 +00:00
}
2004-06-16 01:14:43 +00:00
/* On startup, register the main thread's slot. We do this in a static constructor,
* and not InitThreads(), to guarantee it happens in the main thread. */
2004-02-20 23:42:40 +00:00
static struct SetupMainThread
2003-07-27 03:08:02 +00:00
{
SetupMainThread()
{
2004-06-13 07:09:41 +00:00
LockMut(g_ThreadSlotsLock);
2003-07-27 03:08:02 +00:00
int slot = FindEmptyThreadSlot();
strcpy( g_ThreadSlots[slot].name, "Main thread" );
2004-06-11 19:24:05 +00:00
sprintf( g_ThreadSlots[slot].ThreadFormattedOutput, "Thread: %s", g_ThreadSlots[slot].name );
g_ThreadSlots[slot].pImpl = MakeThisThread();
2004-06-16 01:14:43 +00:00
g_ThreadSlots[slot].id = RageThread::GetCurrentThreadID();
2003-07-27 03:08:02 +00:00
}
} SetupMainThreadObj;
2004-02-20 23:42:40 +00:00
2003-07-27 03:08:02 +00:00
const char *RageThread::GetCurThreadName()
{
2004-04-20 03:44:38 +00:00
return GetThreadNameByID( GetCurrentThreadID() );
}
2004-06-11 19:24:05 +00:00
const char *RageThread::GetThreadNameByID( uint64_t iID )
2004-04-20 03:44:38 +00:00
{
2004-06-11 19:24:05 +00:00
ThreadSlot *slot = GetThreadSlotFromID( iID );
if( slot == NULL )
2004-04-20 03:44:38 +00:00
return "???";
2004-06-11 19:24:05 +00:00
return slot->GetThreadName();
2003-07-27 03:08:02 +00:00
}
2004-09-07 02:58:59 +00:00
bool RageThread::EnumThreadIDs( int n, uint64_t &iID )
{
if( n >= MAX_THREADS )
return false;
LockMut(g_ThreadSlotsLock);
const ThreadSlot *slot = &g_ThreadSlots[n];
if( slot->used )
iID = slot->id;
else
iID = GetInvalidThreadId();
return true;
}
2003-07-27 03:08:02 +00:00
int RageThread::Wait()
{
2004-06-11 19:24:05 +00:00
ASSERT( m_pSlot != NULL );
ASSERT( m_pSlot->pImpl != NULL );
int ret = m_pSlot->pImpl->Wait();
2003-07-27 03:08:02 +00:00
2004-06-13 07:55:27 +00:00
LockMut(g_ThreadSlotsLock);
2004-06-11 19:24:05 +00:00
delete m_pSlot->pImpl;
m_pSlot->pImpl = NULL;
m_pSlot->Init();
m_pSlot = NULL;
2003-07-27 03:08:02 +00:00
2004-06-11 19:24:05 +00:00
return ret;
2004-03-28 06:04:37 +00:00
}
2003-07-27 07:00:13 +00:00
void RageThread::HaltAllThreads( bool Kill )
2003-07-27 03:08:02 +00:00
{
2004-06-11 19:24:05 +00:00
const uint64_t ThisThreadID = GetThisThreadId();
2003-10-01 08:20:03 +00:00
for( int entry = 0; entry < MAX_THREADS; ++entry )
{
if( !g_ThreadSlots[entry].used )
continue;
2004-06-16 01:14:43 +00:00
if( ThisThreadID == g_ThreadSlots[entry].id || g_ThreadSlots[entry].pImpl == NULL )
2004-03-28 06:04:37 +00:00
continue;
2004-06-11 19:24:05 +00:00
g_ThreadSlots[entry].pImpl->Halt( Kill );
2004-03-28 06:04:37 +00:00
}
2004-01-12 01:36:25 +00:00
}
void RageThread::ResumeAllThreads()
{
2004-06-11 19:24:05 +00:00
const uint64_t ThisThreadID = GetThisThreadId();
2004-01-12 01:36:25 +00:00
for( int entry = 0; entry < MAX_THREADS; ++entry )
{
if( !g_ThreadSlots[entry].used )
continue;
2004-06-16 01:14:43 +00:00
if( ThisThreadID == g_ThreadSlots[entry].id || g_ThreadSlots[entry].pImpl == NULL )
2004-01-12 01:36:25 +00:00
continue;
2004-06-11 19:24:05 +00:00
g_ThreadSlots[entry].pImpl->Resume();
2003-10-01 08:20:03 +00:00
}
2003-07-27 03:08:02 +00:00
}
2004-06-11 19:24:05 +00:00
uint64_t RageThread::GetCurrentThreadID()
2004-04-07 03:38:15 +00:00
{
2004-06-11 19:24:05 +00:00
return GetThisThreadId();
2004-04-07 03:38:15 +00:00
}
/* Normally, checkpoints are only seen in crash logs. It's occasionally useful
* to see them in logs, but this outputs a huge amount of text. */
static bool g_LogCheckpoints = false;
void Checkpoints::LogCheckpoints( bool on )
{
g_LogCheckpoints = on;
}
void Checkpoints::SetCheckpoint( const char *file, int line, const char *message )
{
2004-06-11 19:24:05 +00:00
ThreadSlot *slot = GetCurThreadSlot();
if( slot == NULL )
slot = GetUnknownThreadSlot();
/* We can't ASSERT here, since that uses checkpoints. */
2004-06-11 19:24:05 +00:00
if( slot == NULL )
2004-06-16 00:29:52 +00:00
sm_crash( "GetUnknownThreadSlot() returned NULL" );
2004-06-11 19:24:05 +00:00
slot->Checkpoints[slot->CurCheckpoint].Set( file, line, message );
if( g_LogCheckpoints )
2004-06-11 19:24:05 +00:00
LOG->Trace( "%s", slot->Checkpoints[slot->CurCheckpoint].FormattedBuf );
2004-06-11 19:24:05 +00:00
++slot->CurCheckpoint;
slot->NumCheckpoints = max( slot->NumCheckpoints, slot->CurCheckpoint );
slot->CurCheckpoint %= CHECKPOINT_COUNT;
}
/* This is called under crash conditions. Be careful. */
static const char *GetCheckpointLog( int slotno, int lineno )
{
static char ret[1024*32];
ret[0] = 0;
ThreadSlot &slot = g_ThreadSlots[slotno];
if( !slot.used )
return NULL;
2004-02-20 23:42:40 +00:00
/* Only show the "Unknown thread" entry if it has at least one checkpoint. */
2004-06-11 19:24:05 +00:00
if( &slot == g_pUnknownThreadSlot && slot.GetFormattedCheckpoint( 0 ) == NULL )
2004-02-20 23:42:40 +00:00
return NULL;
if( lineno != 0 )
return slot.GetFormattedCheckpoint( lineno-1 );
slot.ThreadFormattedOutput[sizeof(slot.ThreadFormattedOutput)-1] = 0;
strcat(ret, slot.ThreadFormattedOutput);
return ret;
}
const char *Checkpoints::GetLogs( const char *delim )
{
static char ret[1024*32];
ret[0] = 0;
for( int slotno = 0; slotno < MAX_THREADS; ++slotno )
{
const char *buf = GetCheckpointLog( slotno, 0 );
if( buf == NULL )
2004-02-20 23:42:40 +00:00
continue;
strcat( ret, buf );
strcat( ret, delim );
for( int line = 1; (buf = GetCheckpointLog( slotno, line )) != NULL; ++line )
{
strcat( ret, buf );
strcat( ret, delim );
}
}
return ret;
}
/*
* "Safe" mutexes: locking the same mutex more than once from the same thread
* is refcounted and does not deadlock.
*
* Only actually lock the mutex once; when we do so, remember which thread locked it.
* Then, when we lock in the future, only increment a counter, with no locks.
*
* We must be holding the real mutex to write to LockedBy and LockCnt. However,
* we can look at LockedBy to see if it's us that owns it (in which case, we already
* hold the mutex).
*
* In Windows, this helps smooth out performance: for some reason, Windows likes
* to yank the scheduler away from a thread that locks a mutex that it already owns.
*/
#if 0
2004-03-21 08:36:43 +00:00
static const int MAX_MUTEXES = 256;
/* g_MutexesBefore[n] is a list of mutex IDs which must be locked before n (if at all).
* The array g_MutexesBefore[n] is locked for writing by locking mutex n, so lock that
* mutex *before* calling MarkLockedMutex(). */
bool g_MutexesBefore[MAX_MUTEXES][MAX_MUTEXES];
void RageMutex::MarkLockedMutex()
{
/* This only makes locking take about 25% longer, and we generally don't lock in
* inner loops, so this is enabled by default for now. */
// if( !g_bEnableMutexOrderChecking )
// return;
const int ID = this->m_UniqueID;
ASSERT( ID < MAX_MUTEXES );
/* This is a queue of all mutexes that must be locked before ID, if at all. */
vector<const RageMutex *> before;
/* Iterate over all locked mutexes that are locked by this thread. */
unsigned i;
for( i = 0; i < g_MutexList->size(); ++i )
{
const RageMutex *mutex = (*g_MutexList)[i];
if( mutex->m_UniqueID == this->m_UniqueID )
continue;
if( !mutex->IsLockedByThisThread() )
continue;
/* mutex must be locked before this. If we've previously marked the opposite,
* then we have an inconsistent lock order. */
if( g_MutexesBefore[mutex->m_UniqueID][this->m_UniqueID] )
{
2004-03-21 09:30:15 +00:00
LOG->Warn( "Mutex lock inconsistency: mutex \"%s\" must be locked before \"%s\"",
this->GetName().c_str(), mutex->GetName().c_str() );
2004-03-21 08:36:43 +00:00
break;
}
/* Optimization: don't add it to the queue if it's already been done. */
if( !g_MutexesBefore[this->m_UniqueID][mutex->m_UniqueID] )
before.push_back( mutex );
}
while( before.size() )
{
const RageMutex *mutex = before.back();
before.pop_back();
g_MutexesBefore[this->m_UniqueID][mutex->m_UniqueID] = 1;
/* All IDs which must be locked before mutex must also be locked before
* this. That is, if A < mutex, because mutex < this, mark A < this. */
for( i = 0; i < g_MutexList->size(); ++i )
{
const RageMutex *mutex2 = (*g_MutexList)[i];
if( g_MutexesBefore[mutex->m_UniqueID][mutex2->m_UniqueID] )
before.push_back( mutex2 );
}
}
}
2004-03-21 08:36:43 +00:00
/* XXX: How can g_FreeMutexIDs and g_MutexList be threadsafed? */
static set<int> *g_FreeMutexIDs = NULL;
#endif
RageMutex::RageMutex( const CString name ):
m_sName( name )
{
2004-06-11 19:24:05 +00:00
m_pMutex = MakeMutex( this );
m_LockedBy = GetInvalidThreadId();
m_LockCnt = 0;
2004-03-21 08:36:43 +00:00
/* if( g_FreeMutexIDs == NULL )
2004-03-21 08:36:43 +00:00
{
g_FreeMutexIDs = new set<int>;
for( int i = 0; i < MAX_MUTEXES; ++i )
g_FreeMutexIDs->insert( i );
}
2004-04-20 01:08:49 +00:00
if( g_FreeMutexIDs->empty() )
{
ASSERT_M( g_MutexList, "!g_FreeMutexIDs but !g_MutexList?" ); // doesn't make sense to be out of mutexes yet never created any
CString s;
for( unsigned i = 0; i < g_MutexList->size(); ++i )
{
if( i )
s += ", ";
s += ssprintf( "\"%s\"", (*g_MutexList)[i]->GetName().c_str() );
}
LOG->Trace( "%s", s.c_str() );
FAIL_M( ssprintf("MAX_MUTEXES exceeded creating \"%s\"", name.c_str() ) );
}
2004-03-21 08:36:43 +00:00
m_UniqueID = *g_FreeMutexIDs->begin();
2004-03-21 08:36:43 +00:00
g_FreeMutexIDs->erase( g_FreeMutexIDs->begin() );
if( g_MutexList == NULL )
g_MutexList = new vector<RageMutex*>;
g_MutexList->push_back( this );
*/
}
RageMutex::~RageMutex()
{
2004-06-11 19:24:05 +00:00
delete m_pMutex;
/*
2004-03-21 08:36:43 +00:00
vector<RageMutex*>::iterator it = find( g_MutexList->begin(), g_MutexList->end(), this );
ASSERT( it != g_MutexList->end() );
g_MutexList->erase( it );
if( g_MutexList->empty() )
{
delete g_MutexList;
g_MutexList = NULL;
}
2004-06-11 19:24:05 +00:00
delete m_pMutex;
2004-03-21 08:36:43 +00:00
g_FreeMutexIDs->insert( m_UniqueID );
*/
}
void RageMutex::Lock()
{
2004-06-11 19:24:05 +00:00
if( m_LockedBy == (uint64_t) GetThisThreadId() )
{
++m_LockCnt;
return;
}
2004-03-21 09:30:15 +00:00
2004-06-11 19:24:05 +00:00
if( !m_pMutex->Lock() )
{
const ThreadSlot *ThisSlot = GetThreadSlotFromID( GetThisThreadId() );
const ThreadSlot *OtherSlot = GetThreadSlotFromID( m_LockedBy );
CString ThisSlotName = "(???" ")"; // stupid trigraph warnings
CString OtherSlotName = "(???" ")"; // stupid trigraph warnings
if( ThisSlot )
ThisSlotName = ssprintf( "%s (%i)", ThisSlot->GetThreadName(), (int) ThisSlot->id );
if( OtherSlot )
OtherSlotName = ssprintf( "%s (%i)", OtherSlot->GetThreadName(), (int) OtherSlot->id );
const CString sReason = ssprintf( "Thread deadlock on mutex %s between %s and %s",
GetName().c_str(), ThisSlotName.c_str(), OtherSlotName.c_str() );
2004-06-11 19:24:05 +00:00
#if defined(CRASH_HANDLER)
2004-06-13 07:55:27 +00:00
/* Don't leave g_ThreadSlotsLock when we call ForceCrashHandlerDeadlock. */
g_ThreadSlotsLock.Lock();
2004-09-07 02:58:59 +00:00
uint64_t CrashHandle = OtherSlot? OtherSlot->id:0;
2004-06-13 07:55:27 +00:00
g_ThreadSlotsLock.Unlock();
2004-06-11 19:24:05 +00:00
/* Pass the crash handle of the other thread, so it can backtrace that thread. */
2004-06-13 07:55:27 +00:00
ForceCrashHandlerDeadlock( sReason, CrashHandle );
2004-06-11 19:29:03 +00:00
#else
FAIL_M( sReason );
2004-06-11 19:29:03 +00:00
#endif
2004-06-11 19:24:05 +00:00
}
m_LockedBy = GetThisThreadId();
2004-03-21 09:30:15 +00:00
2004-05-05 20:37:14 +00:00
/* This has internal thread safety issues itself (eg. one thread may delete
* a mutex while another locks one); disable for now. */
2004-06-11 19:24:05 +00:00
// MarkLockedMutex();
}
2004-06-14 05:29:12 +00:00
bool RageMutex::TryLock()
{
if( m_LockedBy == (uint64_t) GetThisThreadId() )
{
++m_LockCnt;
return true;
}
if( !m_pMutex->TryLock() )
return false;
m_LockedBy = GetThisThreadId();
return true;
}
void RageMutex::Unlock()
{
2004-06-11 19:24:05 +00:00
if( m_LockCnt )
{
--m_LockCnt;
return;
}
m_LockedBy = GetInvalidThreadId();
m_pMutex->Unlock();
}
2004-03-21 08:36:43 +00:00
bool RageMutex::IsLockedByThisThread() const
{
2004-06-11 19:24:05 +00:00
return m_LockedBy == GetThisThreadId();
2004-03-21 08:36:43 +00:00
}
2004-06-11 19:24:05 +00:00
LockMutex::LockMutex(RageMutex &pMutex, const char *file_, int line_):
mutex(pMutex),
2002-12-21 05:13:45 +00:00
file(file_),
line(line_),
locked_at(RageTimer::GetTimeSinceStart())
{
mutex.Lock();
2002-12-27 22:11:55 +00:00
locked = true;
2002-12-21 05:13:45 +00:00
}
LockMutex::~LockMutex()
{
2002-12-27 22:11:55 +00:00
if(locked)
mutex.Unlock();
2002-12-21 05:13:45 +00:00
}
2002-12-27 22:11:55 +00:00
void LockMutex::Unlock()
{
ASSERT( locked );
2002-12-27 22:11:55 +00:00
locked = false;
mutex.Unlock();
2002-12-27 23:01:45 +00:00
if( file && locked_at != -1 )
2002-12-27 23:01:45 +00:00
{
const float dur = RageTimer::GetTimeSinceStart() - locked_at;
if( dur > 0.015f )
LOG->Trace( "Lock at %s:%i took %f", file, line, dur );
2002-12-27 23:01:45 +00:00
}
2002-12-27 22:11:55 +00:00
}
2004-06-14 05:29:12 +00:00
RageSemaphore::RageSemaphore( CString sName, int iInitialValue ):
m_sName( sName )
{
m_pSema = MakeSemaphore( iInitialValue );
}
RageSemaphore::~RageSemaphore()
{
delete m_pSema;
}
int RageSemaphore::GetValue() const
{
return m_pSema->GetValue();
}
void RageSemaphore::Post()
{
m_pSema->Post();
}
2004-08-29 22:51:09 +00:00
void RageSemaphore::Wait( bool bFailOnTimeout )
2004-06-14 05:29:12 +00:00
{
retry:
2004-06-14 05:29:12 +00:00
if( m_pSema->Wait() )
return;
2004-08-29 22:51:09 +00:00
if( !bFailOnTimeout || Dialog::IsShowingDialog() )
goto retry;
2004-06-14 05:29:12 +00:00
/* We waited too long. We're probably deadlocked, though unlike mutexes, we can't
* tell which thread we're stuck on. */
const ThreadSlot *ThisSlot = GetThreadSlotFromID( GetThisThreadId() );
const CString sReason = ssprintf( "Semaphore timeout on mutex %s on thread %s",
GetName().c_str(), ThisSlot? ThisSlot->GetThreadName(): "(???" ")" ); // stupid trigraph warnings
2004-06-14 10:57:21 +00:00
#if defined(CRASH_HANDLER)
2004-09-07 03:17:56 +00:00
ForceCrashHandlerDeadlock( sReason, GetInvalidThreadId() );
2004-06-14 05:29:12 +00:00
#else
RageException::Throw( "%s", sReason.c_str() );
#endif
}
bool RageSemaphore::TryWait()
{
return m_pSema->TryWait();
}
/*
2004-05-06 02:40:33 +00:00
* Copyright (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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.
*/