smsvn -> ssc-hg glue: rearrange directory structure
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
Reference in New Issue
Block a user