smsvn -> ssc-hg glue: rearrange directory structure

This commit is contained in:
Devin J. Pohly
2013-06-10 15:38:43 -04:00
parent 51576d5942
commit 80057f53cd
3362 changed files with 0 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
#ifndef THREADS_H
#define THREADS_H
/* This is the low-level implementation; you probably want RageThreads. */
class RageMutex;
class RageTimer;
class ThreadImpl
{
public:
virtual ~ThreadImpl() { }
virtual void Halt( bool Kill ) = 0;
virtual void Resume() = 0;
/* Get the identifier for this thread. The actual meaning of this is implementation-
* defined, except that each thread has exactly one ID and each ID corresponds to
* one thread. (This means that Win32 thread handles are not acceptable as ThreadIds.) */
virtual uint64_t GetThreadId() const = 0;
virtual int Wait() = 0;
};
class MutexImpl
{
public:
RageMutex *m_Parent;
MutexImpl( RageMutex *pParent ) { m_Parent = pParent; }
virtual ~MutexImpl() { }
/* Lock the mutex. If mutex timeouts are implemented, and the mutex times out,
* return false and do not lock the mutex. No other failure return is allowed;
* all other errors should fail with an assertion. */
virtual bool Lock() = 0;
/* Non-blocking lock. If locking the mutex would block because the mutex is already
* locked by another thread, return false; otherwise return true and lock the mutex. */
virtual bool TryLock() = 0;
/* Unlock the mutex. This must only be called when the mutex is locked; implementations
* may fail with an assertion if the mutex is not locked. */
virtual void Unlock() = 0;
};
class EventImpl
{
public:
virtual ~EventImpl() { }
virtual bool Wait( RageTimer *pTimeout ) = 0;
virtual void Signal() = 0;
virtual void Broadcast() = 0;
virtual bool WaitTimeoutSupported() const = 0;
};
class SemaImpl
{
public:
virtual ~SemaImpl() { }
virtual int GetValue() const = 0;
virtual void Post() = 0;
virtual bool Wait() = 0;
virtual bool TryWait() = 0;
};
/* These functions must be implemented by the thread implementation. */
ThreadImpl *MakeThread( int (*fn)(void *), void *data, uint64_t *piThreadID );
ThreadImpl *MakeThisThread();
MutexImpl *MakeMutex( RageMutex *pParent );
EventImpl *MakeEvent( MutexImpl *pMutex );
SemaImpl *MakeSemaphore( int iInitialValue );
uint64_t GetThisThreadId();
/* Since ThreadId is implementation-defined, we can't define a universal invalid
* value. Return the invalid value for this implementation. */
uint64_t GetInvalidThreadId();
#endif
/*
* (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.
*/
+566
View File
@@ -0,0 +1,566 @@
#include "global.h"
#include "Threads_Pthreads.h"
#include "RageTimer.h"
#include "RageUtil.h"
#include <sys/time.h>
#include <errno.h>
#if defined(UNIX)
#include "archutils/Unix/LinuxThreadHelpers.h"
#include "archutils/Unix/RunningUnderValgrind.h"
#endif
#if defined(MACOSX)
#include "archutils/Darwin/DarwinThreadHelpers.h"
#endif
void ThreadImpl_Pthreads::Halt( bool Kill )
{
/* Linux:
* Send a SIGSTOP to the thread. If we send a SIGKILL, pthreads will
* "helpfully" propagate it to the other threads, and we'll get killed, too.
*
* This isn't ideal, since it can cause the process to background as far as
* the shell is concerned, so the shell prompt can display before the crash
* handler actually displays a message.
*/
SuspendThread( threadHandle );
}
void ThreadImpl_Pthreads::Resume()
{
/* Linux: Send a SIGCONT to the thread. */
ResumeThread( threadHandle );
}
uint64_t ThreadImpl_Pthreads::GetThreadId() const
{
return threadHandle;
}
int ThreadImpl_Pthreads::Wait()
{
int *val;
int ret = pthread_join( thread, (void **) &val );
ASSERT_M( ret == 0, ssprintf("pthread_join: %s", strerror(ret)) );
int iRet = *val;
delete val;
return iRet;
}
ThreadImpl *MakeThisThread()
{
ThreadImpl_Pthreads *thread = new ThreadImpl_Pthreads;
thread->thread = pthread_self();
thread->threadHandle = GetCurrentThreadId();
return thread;
}
static void *StartThread( void *pData )
{
ThreadImpl_Pthreads *pThis = (ThreadImpl_Pthreads *) pData;
pThis->threadHandle = GetCurrentThreadId();
*pThis->m_piThreadID = pThis->threadHandle;
/* Tell MakeThread that we've set m_piThreadID, so it's safe to return. */
pThis->m_StartFinishedSem->Post();
int iRet = pThis->m_pFunc( pThis->m_pData );
return new int(iRet);
}
ThreadImpl *MakeThread( int (*pFunc)(void *pData), void *pData, uint64_t *piThreadID )
{
ThreadImpl_Pthreads *thread = new ThreadImpl_Pthreads;
thread->m_pFunc = pFunc;
thread->m_pData = pData;
thread->m_piThreadID = piThreadID;
thread->m_StartFinishedSem = new SemaImpl_Pthreads( 0 );
int ret = pthread_create( &thread->thread, NULL, StartThread, thread );
ASSERT_M( ret == 0, ssprintf( "MakeThread: pthread_create: %s", strerror(errno)) );
/* Don't return until StartThread sets m_piThreadID. */
thread->m_StartFinishedSem->Wait();
delete thread->m_StartFinishedSem;
return thread;
}
MutexImpl_Pthreads::MutexImpl_Pthreads( RageMutex *pParent ):
MutexImpl( pParent )
{
pthread_mutex_init( &mutex, NULL );
}
MutexImpl_Pthreads::~MutexImpl_Pthreads()
{
int ret = pthread_mutex_destroy( &mutex ) == -1;
ASSERT_M( ret == 0, ssprintf("Error deleting mutex: %s", strerror(errno)) );
}
#if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) || defined(HAVE_PTHREAD_COND_TIMEDWAIT)
static bool UseTimedlock()
{
#if defined(LINUX)
/* Valgrind crashes and burns on pthread_mutex_timedlock. */
if( RunningUnderValgrind() )
return false;
#endif
return true;
}
#endif
bool MutexImpl_Pthreads::Lock()
{
#if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)
if( UseTimedlock() )
{
int len = 10; /* seconds */
int tries = 2;
while( tries-- )
{
/* Wait for ten seconds. If it takes longer than that, we're
* probably deadlocked. */
timeval tv;
gettimeofday( &tv, NULL );
timespec ts;
ts.tv_sec = tv.tv_sec + len;
ts.tv_nsec = tv.tv_usec * 1000;
int ret = pthread_mutex_timedlock( &mutex, &ts );
switch( ret )
{
case 0:
return true;
case EINTR:
/* Ignore it. */
++tries;
continue;
case ETIMEDOUT:
/* Timed out. Probably deadlocked. Try again one more time, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
len = 1;
break;
default:
FAIL_M( ssprintf("pthread_mutex_timedlock: %s", strerror(errno)) );
}
}
return false;
}
#endif
int ret;
do
{
ret = pthread_mutex_lock( &mutex );
}
while( ret == -1 && ret == EINTR );
ASSERT_M( ret == 0, ssprintf("pthread_mutex_lock: %s", strerror(errno)) );
return true;
}
bool MutexImpl_Pthreads::TryLock()
{
int ret = pthread_mutex_trylock( &mutex );
if( ret == EBUSY )
return false;
ASSERT_M( ret == 0, ssprintf("pthread_mutex_trylock failed: %s", strerror(errno)) );
return true;
}
void MutexImpl_Pthreads::Unlock()
{
pthread_mutex_unlock( &mutex );
}
uint64_t GetThisThreadId()
{
return GetCurrentThreadId();
}
uint64_t GetInvalidThreadId()
{
return 0;
}
MutexImpl *MakeMutex( RageMutex *pParent )
{
return new MutexImpl_Pthreads( pParent );
}
/* Check if condattr_setclock is supported, and supports the clock that RageTimer
* selected. */
#if defined(UNIX)
#include <dlfcn.h>
#include "arch/ArchHooks/ArchHooks_Unix.h"
#else
typedef int clockid_t;
static const clockid_t CLOCK_REALTIME = 0;
static const clockid_t CLOCK_MONOTONIC = 1;
#endif
namespace
{
typedef int (* CONDATTR_SET_CLOCK)( pthread_condattr_t *attr, clockid_t clock_id );
CONDATTR_SET_CLOCK g_CondattrSetclock = NULL;
bool bInitialized = false;
#if defined(UNIX)
clockid_t GetClock()
{
return ArchHooks_Unix::GetClock();
}
void InitMonotonic()
{
if( bInitialized )
return;
bInitialized = true;
void *pLib = NULL;
do {
{
pLib = dlopen( NULL, RTLD_LAZY );
if( pLib == NULL )
break;
g_CondattrSetclock = (CONDATTR_SET_CLOCK) dlsym( pLib, "pthread_condattr_setclock" );
if( g_CondattrSetclock == NULL )
break;
}
/* Make sure that we can set up the clock attribute. */
pthread_condattr_t condattr;
pthread_condattr_init( &condattr );
if( g_CondattrSetclock(&condattr, GetClock()) != 0 )
{
printf( "pthread_condattr_setclock failed\n" );
pthread_condattr_destroy( &condattr );
break;
}
pthread_condattr_destroy( &condattr );
/* Everything seems to work. */
return;
} while(0);
g_CondattrSetclock = NULL;
if( pLib != NULL )
dlclose( pLib );
pLib = NULL;
}
#elif defined(MACOSX)
void InitMonotonic() { bInitialized = true; }
clockid_t GetClock() { return CLOCK_MONOTONIC; }
#else
void InitMonotonic()
{
bInitialized = true;
}
clockid_t GetClock()
{
return CLOCK_REALTIME;
}
#endif
};
EventImpl_Pthreads::EventImpl_Pthreads( MutexImpl_Pthreads *pParent )
{
m_pParent = pParent;
InitMonotonic();
pthread_condattr_t condattr;
pthread_condattr_init( &condattr );
if( g_CondattrSetclock != NULL )
g_CondattrSetclock( &condattr, GetClock() );
pthread_cond_init( &m_Cond, &condattr );
pthread_condattr_destroy( &condattr );
}
EventImpl_Pthreads::~EventImpl_Pthreads()
{
pthread_cond_destroy( &m_Cond );
}
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT)
bool EventImpl_Pthreads::Wait( RageTimer *pTimeout )
{
if( pTimeout == NULL )
{
pthread_cond_wait( &m_Cond, &m_pParent->mutex );
return true;
}
/* If the clock is not CLOCK_MONOTONIC, or we can't change the wait clock (no
* condattr_setclock), pthread_cond_timedwait has an inherent race condition:
* the system clock may change before we call it. */
timespec abstime;
if( g_CondattrSetclock != NULL || GetClock() == CLOCK_REALTIME )
{
/* If we support condattr_setclock, we'll set the condition to use the same
* clock as RageTimer and can use it directly. If the clock is CLOCK_REALTIME,
* that's the default anyway. */
abstime.tv_sec = pTimeout->m_secs;
abstime.tv_nsec = pTimeout->m_us * 1000;
}
else
{
/* The RageTimer clock is different than the wait clock; convert it. */
timeval tv;
gettimeofday( &tv, NULL );
RageTimer timeofday( tv.tv_sec, tv.tv_usec );
float fSecondsInFuture = -pTimeout->Ago();
timeofday += fSecondsInFuture;
abstime.tv_sec = timeofday.m_secs;
abstime.tv_nsec = timeofday.m_us * 1000;
}
int iRet = pthread_cond_timedwait( &m_Cond, &m_pParent->mutex, &abstime );
return iRet != ETIMEDOUT;
}
bool EventImpl_Pthreads::WaitTimeoutSupported() const
{
return true;
}
#else
bool EventImpl_Pthreads::Wait( RageTimer *pTimeout )
{
pthread_cond_wait( &m_Cond, &m_pParent->mutex );
return true;
}
bool EventImpl_Pthreads::WaitTimeoutSupported() const
{
return false;
}
#endif
void EventImpl_Pthreads::Signal()
{
pthread_cond_signal( &m_Cond );
}
void EventImpl_Pthreads::Broadcast()
{
pthread_cond_broadcast( &m_Cond );
}
EventImpl *MakeEvent( MutexImpl *pMutex )
{
MutexImpl_Pthreads *pPthreadsMutex = (MutexImpl_Pthreads *) pMutex;
return new EventImpl_Pthreads( pPthreadsMutex );
}
#if 0
SemaImpl_Pthreads::SemaImpl_Pthreads( int iInitialValue )
{
sem_init( &sem, 0, iInitialValue );
}
SemaImpl_Pthreads::~SemaImpl_Pthreads()
{
sem_destroy( &sem );
}
int SemaImpl_Pthreads::GetValue() const
{
int ret;
sem_getvalue( const_cast<sem_t *>(&sem), &ret );
return ret;
}
void SemaImpl_Pthreads::Post()
{
sem_post( &sem );
}
bool SemaImpl_Pthreads::Wait()
{
int ret;
do
{
ret = sem_wait( &sem );
}
while( ret == -1 && errno == EINTR );
ASSERT_M( ret == 0, ssprintf("Wait: sem_wait: %s", strerror(errno)) );
return true;
}
bool SemaImpl_Pthreads::TryWait()
{
int ret = sem_trywait( &sem );
if( ret == -1 && errno == EAGAIN )
return false;
ASSERT_M( ret == 0, ssprintf("TryWait: sem_trywait failed: %s", strerror(errno)) );
return true;
}
#else
/* Use conditions, to work around OS X "forgetting" to implement semaphores. */
SemaImpl_Pthreads::SemaImpl_Pthreads( int iInitialValue )
{
int ret = pthread_cond_init( &m_Cond, NULL );
ASSERT_M( ret == 0, ssprintf( "SemaImpl_Pthreads: pthread_cond_init: %s", strerror(errno)) );
ret = pthread_mutex_init( &m_Mutex, NULL );
ASSERT_M( ret == 0, ssprintf( "SemaImpl_Pthreads: pthread_mutex_init: %s", strerror(errno)) );
m_iValue = iInitialValue;
}
SemaImpl_Pthreads::~SemaImpl_Pthreads()
{
pthread_cond_destroy( &m_Cond );
pthread_mutex_destroy( &m_Mutex );
}
void SemaImpl_Pthreads::Post()
{
pthread_mutex_lock( &m_Mutex );
++m_iValue;
if( m_iValue == 1 )
pthread_cond_signal( &m_Cond );
pthread_mutex_unlock( &m_Mutex );
}
bool SemaImpl_Pthreads::Wait()
{
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT)
if( UseTimedlock() )
{
timeval tv;
gettimeofday( &tv, NULL );
/* Wait for ten seconds. If it takes longer than that, we're probably deadlocked. */
timespec ts;
ts.tv_sec = tv.tv_sec + 10;
ts.tv_nsec = tv.tv_usec * 1000;
pthread_mutex_lock( &m_Mutex );
int tries = 2;
while( !m_iValue && tries )
{
int ret = pthread_cond_timedwait( &m_Cond, &m_Mutex, &ts );
switch( ret )
{
case 0:
case EINTR:
break;
case ETIMEDOUT:
/* Timed out. Probably deadlocked. Try again one more time, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
++ts.tv_sec;
tries--;
break;
default:
FAIL_M( ssprintf("pthread_mutex_timedlock: %s", strerror(errno)) );
}
}
if( !m_iValue )
{
/* Timed out. */
pthread_mutex_unlock( &m_Mutex );
return false;
}
else
{
--m_iValue;
pthread_mutex_unlock( &m_Mutex );
return true;
}
}
#endif
pthread_mutex_lock( &m_Mutex );
while( !m_iValue )
pthread_cond_wait( &m_Cond, &m_Mutex );
--m_iValue;
pthread_mutex_unlock( &m_Mutex);
return true;
}
bool SemaImpl_Pthreads::TryWait()
{
pthread_mutex_lock( &m_Mutex );
if( !m_iValue )
{
pthread_mutex_unlock( &m_Mutex);
return false;
}
--m_iValue;
pthread_mutex_unlock( &m_Mutex);
return true;
}
#endif
SemaImpl *MakeSemaphore( int iInitialValue )
{
return new SemaImpl_Pthreads( iInitialValue );
}
/*
* (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.
*/
+123
View File
@@ -0,0 +1,123 @@
#ifndef THREADS_PTHREADS_H
#define THREADS_PTHREADS_H
#include "Threads.h"
#include <pthread.h>
#include <semaphore.h>
class ThreadImpl_Pthreads: public ThreadImpl
{
public:
pthread_t thread;
/* Linux:
* Keep a list of child PIDs, so we can send them SIGKILL. This has an
* added bonus: if this is corrupted, we'll just send signals and they'll
* fail; we won't blow up (unless we're root).
*/
uint64_t threadHandle;
/* These are only used during initialization. */
int (*m_pFunc)( void *pData );
void *m_pData;
uint64_t *m_piThreadID;
SemaImpl *m_StartFinishedSem;
void Halt( bool Kill );
void Resume();
uint64_t GetThreadId() const;
int Wait();
};
class MutexImpl_Pthreads: public MutexImpl
{
friend class EventImpl_Pthreads;
public:
MutexImpl_Pthreads( RageMutex *parent );
~MutexImpl_Pthreads();
bool Lock();
bool TryLock();
void Unlock();
protected:
pthread_mutex_t mutex;
};
class EventImpl_Pthreads: public EventImpl
{
public:
EventImpl_Pthreads( MutexImpl_Pthreads *pParent );
~EventImpl_Pthreads();
bool Wait( RageTimer *pTimeout );
void Signal();
void Broadcast();
bool WaitTimeoutSupported() const;
private:
MutexImpl_Pthreads *m_pParent;
pthread_cond_t m_Cond;
};
#if 0
class SemaImpl_Pthreads: public SemaImpl
{
public:
SemaImpl_Pthreads( int iInitialValue );
~SemaImpl_Pthreads();
int GetValue() const;
void Post();
bool Wait();
bool TryWait();
private:
sem_t sem;
};
#else
class SemaImpl_Pthreads: public SemaImpl
{
public:
SemaImpl_Pthreads( int iInitialValue );
~SemaImpl_Pthreads();
int GetValue() const { return m_iValue; }
void Post();
bool Wait();
bool TryWait();
private:
pthread_cond_t m_Cond;
pthread_mutex_t m_Mutex;
unsigned m_iValue;
};
#endif
#endif
/*
* (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.
*/
+512
View File
@@ -0,0 +1,512 @@
#include "global.h"
#include "Threads_Win32.h"
#include "RageUtil.h"
#include "RageThreads.h"
#include "RageTimer.h"
#include "archutils/Win32/ErrorStrings.h"
const int MAX_THREADS=128;
static MutexImpl_Win32 *g_pThreadIdMutex = NULL;
static void InitThreadIdMutex()
{
if( g_pThreadIdMutex != NULL )
return;
g_pThreadIdMutex = new MutexImpl_Win32(NULL);
}
static uint64_t g_ThreadIds[MAX_THREADS];
static HANDLE g_ThreadHandles[MAX_THREADS];
HANDLE Win32ThreadIdToHandle( uint64_t iID )
{
for( int i = 0; i < MAX_THREADS; ++i )
{
if( g_ThreadIds[i] == iID )
return g_ThreadHandles[i];
}
return NULL;
}
void ThreadImpl_Win32::Halt( bool Kill )
{
#ifndef _XBOX
if( Kill )
TerminateThread( ThreadHandle, 0 );
else
#endif
SuspendThread( ThreadHandle );
}
void ThreadImpl_Win32::Resume()
{
ResumeThread( ThreadHandle );
}
uint64_t ThreadImpl_Win32::GetThreadId() const
{
return (uint64_t) ThreadId;
}
int ThreadImpl_Win32::Wait()
{
WaitForSingleObject( ThreadHandle, INFINITE );
DWORD ret;
GetExitCodeThread( ThreadHandle, &ret );
CloseHandle( ThreadHandle );
ThreadHandle = NULL;
return ret;
}
/* SetThreadName magic comes from VirtualDub. */
#define MS_VC_EXCEPTION 0x406d1388
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in same addr space)
DWORD dwThreadID; // thread ID (-1 caller thread)
DWORD dwFlags; // reserved for future use, most be zero
} THREADNAME_INFO;
static void SetThreadName( DWORD dwThreadID, LPCTSTR szThreadName )
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = szThreadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
} __except (EXCEPTION_CONTINUE_EXECUTION) {
}
}
static DWORD WINAPI StartThread( LPVOID pData )
{
ThreadImpl_Win32 *pThis = (ThreadImpl_Win32 *) pData;
SetThreadName( GetCurrentThreadId(), RageThread::GetCurrentThreadName() );
DWORD ret = (DWORD) pThis->m_pFunc( pThis->m_pData );
for( int i = 0; i < MAX_THREADS; ++i )
{
if( g_ThreadIds[i] == RageThread::GetCurrentThreadID() )
{
g_ThreadHandles[i] = NULL;
g_ThreadIds[i] = 0;
break;
}
}
return ret;
}
static int GetOpenSlot( uint64_t iID )
{
InitThreadIdMutex();
g_pThreadIdMutex->Lock();
/* Find an open slot in g_ThreadIds. */
int slot = 0;
while( slot < MAX_THREADS && g_ThreadIds[slot] != 0 )
++slot;
ASSERT( slot < MAX_THREADS );
g_ThreadIds[slot] = iID;
g_pThreadIdMutex->Unlock();
return slot;
}
ThreadImpl *MakeThisThread()
{
ThreadImpl_Win32 *thread = new ThreadImpl_Win32;
SetThreadName( GetCurrentThreadId(), RageThread::GetCurrentThreadName() );
const HANDLE CurProc = GetCurrentProcess();
int ret = DuplicateHandle( CurProc, GetCurrentThread(), CurProc,
&thread->ThreadHandle, 0, false, DUPLICATE_SAME_ACCESS );
if( !ret )
{
// LOG->Warn( werr_ssprintf( GetLastError(), "DuplicateHandle(%p, %p) failed",
// CurProc, GetCurrentThread() ) );
thread->ThreadHandle = NULL;
}
thread->ThreadId = GetCurrentThreadId();
int slot = GetOpenSlot( GetCurrentThreadId() );
g_ThreadHandles[slot] = thread->ThreadHandle;
return thread;
}
ThreadImpl *MakeThread( int (*pFunc)(void *pData), void *pData, uint64_t *piThreadID )
{
ThreadImpl_Win32 *thread = new ThreadImpl_Win32;
thread->m_pFunc = pFunc;
thread->m_pData = pData;
thread->ThreadHandle = CreateThread( NULL, 0, &StartThread, thread, CREATE_SUSPENDED, &thread->ThreadId );
*piThreadID = (uint64_t) thread->ThreadId;
ASSERT_M( thread->ThreadHandle, ssprintf("%s", werr_ssprintf(GetLastError(), "CreateThread")) );
int slot = GetOpenSlot( thread->ThreadId );
g_ThreadHandles[slot] = thread->ThreadHandle;
int iRet = ResumeThread( thread->ThreadHandle );
ASSERT_M( iRet == 1, ssprintf("%s", werr_ssprintf(GetLastError(), "ResumeThread")) );
return thread;
}
MutexImpl_Win32::MutexImpl_Win32( RageMutex *pParent ):
MutexImpl( pParent )
{
mutex = CreateMutex( NULL, false, NULL );
ASSERT_M( mutex != NULL, werr_ssprintf(GetLastError(), "CreateMutex") );
}
MutexImpl_Win32::~MutexImpl_Win32()
{
CloseHandle( mutex );
}
static bool SimpleWaitForSingleObject( HANDLE h, DWORD ms )
{
ASSERT( h != NULL );
DWORD ret = WaitForSingleObject( h, ms );
switch( ret )
{
case WAIT_OBJECT_0:
return true;
case WAIT_TIMEOUT:
return false;
case WAIT_ABANDONED:
/* The docs aren't particular about what this does, but it should never happen. */
FAIL_M( "WAIT_ABANDONED" );
case WAIT_FAILED:
FAIL_M( werr_ssprintf(GetLastError(), "WaitForSingleObject") );
default:
FAIL_M( "unknown" );
}
}
bool MutexImpl_Win32::Lock()
{
int len = 15000;
int tries = 5;
while( tries-- )
{
/* Wait for fifteen seconds. If it takes longer than that, we're probably deadlocked. */
if( SimpleWaitForSingleObject( mutex, len ) )
return true;
/* Timed out; probably deadlocked. Try a couple more times, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
len = 1000;
}
return false;
}
bool MutexImpl_Win32::TryLock()
{
return SimpleWaitForSingleObject( mutex, 0 );
}
void MutexImpl_Win32::Unlock()
{
const bool ret = !!ReleaseMutex( mutex );
/* We can't ASSERT here, since this is called from checkpoints, which is
* called from ASSERT. */
if( !ret )
sm_crash( werr_ssprintf( GetLastError(), "ReleaseMutex failed" ) );
}
uint64_t GetThisThreadId()
{
return GetCurrentThreadId();
}
uint64_t GetInvalidThreadId()
{
return 0;
}
MutexImpl *MakeMutex( RageMutex *pParent )
{
return new MutexImpl_Win32( pParent );
}
EventImpl_Win32::EventImpl_Win32( MutexImpl_Win32 *pParent )
{
m_pParent = pParent;
m_iNumWaiting = 0;
m_WakeupSema = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
InitializeCriticalSection( &m_iNumWaitingLock );
m_WaitersDone = CreateEvent( NULL, FALSE, FALSE, NULL );
}
EventImpl_Win32::~EventImpl_Win32()
{
ASSERT_M( m_iNumWaiting == 0, ssprintf("event destroyed while still in use (%i)", m_iNumWaiting) );
/* We don't own m_pParent; don't free it. */
CloseHandle( m_WakeupSema );
DeleteCriticalSection( &m_iNumWaitingLock );
CloseHandle( m_WaitersDone );
}
/* SignalObjectAndWait is atomic, which leads to more fair event handling. However,
* we don't guarantee or depend upon fair events, and SignalObjectAndWait is only
* available in NT. I also can't find a single function to signal an object like
* SignalObjectAndWait, so we need to know if the object is a mutex or an event. */
static bool PortableSignalObjectAndWait( HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, bool bFirstParamIsMutex, unsigned iMilliseconds = INFINITE )
{
static bool bSignalObjectAndWaitUnavailable = false;
/* Watch out: SignalObjectAndWait doesn't work when iMilliseconds is zero. */
if( !bSignalObjectAndWaitUnavailable && iMilliseconds != 0 )
{
DWORD ret = SignalObjectAndWait( hObjectToSignal, hObjectToWaitOn, iMilliseconds, false );
switch( ret )
{
case WAIT_OBJECT_0:
return true;
case WAIT_ABANDONED:
/* The docs aren't particular about what this does, but it should never happen. */
FAIL_M( "WAIT_ABANDONED" );
case 1: /* bogus Win98 return value */
case WAIT_FAILED:
if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED )
{
/* We're probably on 9x. */
bSignalObjectAndWaitUnavailable = true;
break;
}
FAIL_M( werr_ssprintf(GetLastError(), "SignalObjectAndWait") );
case WAIT_TIMEOUT:
return false;
default:
FAIL_M( ssprintf("Unexpected code from SignalObjectAndWait: %d",ret ));
}
}
if( bFirstParamIsMutex )
{
const bool bRet = !!ReleaseMutex( hObjectToSignal );
if( !bRet )
sm_crash( werr_ssprintf( GetLastError(), "ReleaseMutex failed" ) );
}
else
SetEvent( hObjectToSignal );
DWORD ret = WaitForSingleObject( hObjectToWaitOn, iMilliseconds );
switch( ret )
{
case WAIT_OBJECT_0:
return true;
case WAIT_ABANDONED:
/* The docs aren't particular about what this does, but it should never happen. */
FAIL_M( "WAIT_ABANDONED" );
case WAIT_TIMEOUT:
return false;
default:
FAIL_M( "unknown" );
}
}
/* Event logic from http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. */
bool EventImpl_Win32::Wait( RageTimer *pTimeout )
{
EnterCriticalSection( &m_iNumWaitingLock );
++m_iNumWaiting;
LeaveCriticalSection( &m_iNumWaitingLock );
unsigned iMilliseconds = INFINITE;
if( pTimeout != NULL )
{
float fSecondsInFuture = -pTimeout->Ago();
iMilliseconds = (unsigned) max( 0, int( fSecondsInFuture * 1000 ) );
}
/* Unlock the mutex and wait for a signal. */
bool bSuccess = PortableSignalObjectAndWait( m_pParent->mutex, m_WakeupSema, true, iMilliseconds );
EnterCriticalSection( &m_iNumWaitingLock );
if( !bSuccess )
{
/* Avoid a race condition: someone may have signalled the object between PortableSignalObjectAndWait
* and EnterCriticalSection. While we hold m_iNumWaitingLock, poll (with a zero timeout) the
* object one last time. */
if( WaitForSingleObject( m_WakeupSema, 0 ) == WAIT_OBJECT_0 )
bSuccess = true;
}
--m_iNumWaiting;
bool bLastWaiting = m_iNumWaiting == 0;
LeaveCriticalSection( &m_iNumWaitingLock );
/* If we're the last waiter to wake up, and we were actually woken by another
* thread (not by timeout), wake up the signaller. */
if( bLastWaiting && bSuccess )
PortableSignalObjectAndWait( m_WaitersDone, m_pParent->mutex, false );
else
WaitForSingleObject( m_pParent->mutex, INFINITE );
return bSuccess;
}
void EventImpl_Win32::Signal()
{
EnterCriticalSection( &m_iNumWaitingLock );
if( m_iNumWaiting == 0 )
{
LeaveCriticalSection( &m_iNumWaitingLock );
return;
}
ReleaseSemaphore( m_WakeupSema, 1, 0 );
LeaveCriticalSection( &m_iNumWaitingLock );
/* The waiter will touch m_WaitersDone. */
WaitForSingleObject( m_WaitersDone, INFINITE );
}
void EventImpl_Win32::Broadcast()
{
EnterCriticalSection( &m_iNumWaitingLock );
if( m_iNumWaiting == 0 )
{
LeaveCriticalSection( &m_iNumWaitingLock );
return;
}
ReleaseSemaphore( m_WakeupSema, m_iNumWaiting, 0 );
LeaveCriticalSection( &m_iNumWaitingLock );
/* The last waiter will touch m_WaitersDone, so we wait for all waiters to wake up and
* start waiting for the mutex before returning. */
WaitForSingleObject( m_WaitersDone, INFINITE );
}
EventImpl *MakeEvent( MutexImpl *pMutex )
{
MutexImpl_Win32 *pWin32Mutex = (MutexImpl_Win32 *) pMutex;
return new EventImpl_Win32( pWin32Mutex );
}
SemaImpl_Win32::SemaImpl_Win32( int iInitialValue )
{
sem = CreateSemaphore( NULL, iInitialValue, 999999999, NULL );
m_iCounter = iInitialValue;
}
SemaImpl_Win32::~SemaImpl_Win32()
{
CloseHandle( sem );
}
void SemaImpl_Win32::Post()
{
++m_iCounter;
ReleaseSemaphore( sem, 1, NULL );
}
bool SemaImpl_Win32::Wait()
{
int len = 15000;
int tries = 5;
while( tries-- )
{
/* Wait for 15 seconds. If it takes longer than that, we're
* probably deadlocked. */
if( SimpleWaitForSingleObject( sem, len ) )
{
--m_iCounter;
return true;
}
/* Timed out; probably deadlocked. Try again a few more times, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
len = 1000;
}
return false;
}
bool SemaImpl_Win32::TryWait()
{
if( !SimpleWaitForSingleObject( sem, 0 ) )
return false;
--m_iCounter;
return true;
}
SemaImpl *MakeSemaphore( int iInitialValue )
{
return new SemaImpl_Win32( iInitialValue );
}
/*
* (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.
*/
+105
View File
@@ -0,0 +1,105 @@
#ifndef THREADS_WIN32_H
#define THREADS_WIN32_H
#include "Threads.h"
#if defined(_WINDOWS)
# include <windows.h>
#else if
# include <windef.h>
#endif
class ThreadImpl_Win32: public ThreadImpl
{
public:
HANDLE ThreadHandle;
DWORD ThreadId;
int (*m_pFunc)( void *pData );
void *m_pData;
void Halt( bool Kill );
void Resume();
uint64_t GetThreadId() const;
int Wait();
};
HANDLE Win32ThreadIdToHandle( uint64_t iID );
class MutexImpl_Win32: public MutexImpl
{
friend class EventImpl_Win32;
public:
MutexImpl_Win32( RageMutex *parent );
~MutexImpl_Win32();
bool Lock();
bool TryLock();
void Unlock();
private:
HANDLE mutex;
};
class EventImpl_Win32: public EventImpl
{
public:
EventImpl_Win32( MutexImpl_Win32 *pParent );
~EventImpl_Win32();
bool Wait( RageTimer *pTimeout );
void Signal();
void Broadcast();
bool WaitTimeoutSupported() const { return true; }
private:
MutexImpl_Win32 *m_pParent;
int m_iNumWaiting;
CRITICAL_SECTION m_iNumWaitingLock;
HANDLE m_WakeupSema;
HANDLE m_WaitersDone;
};
class SemaImpl_Win32: public SemaImpl
{
public:
SemaImpl_Win32( int iInitialValue );
~SemaImpl_Win32();
int GetValue() const { return m_iCounter; }
void Post();
bool Wait();
bool TryWait();
private:
HANDLE sem;
/* We have to track the count ourself, since Windows gives no way to query it. */
int m_iCounter;
};
#endif
/*
* (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.
*/