add TryLock
add semaphores
This commit is contained in:
@@ -7,17 +7,6 @@ class RageMutex;
|
||||
|
||||
class ThreadImpl
|
||||
{
|
||||
#if defined(PID_BASED_THREADS)
|
||||
/* 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). */
|
||||
// int pid;
|
||||
#endif
|
||||
|
||||
#if defined(DARWIN)
|
||||
// thread_act_t ThreadHandle;
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual ~ThreadImpl() { }
|
||||
virtual void Halt( bool Kill ) = 0;
|
||||
@@ -46,15 +35,30 @@ public:
|
||||
* 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 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 );
|
||||
ThreadImpl *MakeThisThread();
|
||||
MutexImpl *MakeMutex( RageMutex *pParent );
|
||||
SemaImpl *MakeSemaphore( int iInitialValue );
|
||||
uint64_t GetThisThreadId();
|
||||
|
||||
/* Since ThreadId is implementation-defined, we can't define a universal invalid
|
||||
|
||||
@@ -145,6 +145,11 @@ bool MutexImpl_Pthreads::Lock()
|
||||
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
|
||||
@@ -153,19 +158,35 @@ bool MutexImpl_Pthreads::Lock()
|
||||
break;
|
||||
|
||||
default:
|
||||
RageException::Throw( "pthread_mutex_timedlock: %s", strerror(ret) );
|
||||
FAIL_M( ssprintf("pthread_mutex_timedlock: %s", strerror(ret)) );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
int ret = pthread_mutex_lock( &mutex );
|
||||
if( ret )
|
||||
RageException::Throw( "pthread_mutex_lock failed: %s", strerror(ret) );
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
CHECKPOINT;
|
||||
ret = pthread_mutex_lock( &mutex );
|
||||
}
|
||||
while( ret == -1 && ret == EINTR );
|
||||
|
||||
ASSERT_M( ret == 0, ssprintf("pthread_mutex_lock: %s", strerror(errno)) );
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MutexImpl_Pthreads::TryLock()
|
||||
{
|
||||
int ret = pthread_mutex_trylock( &mutex );
|
||||
if( ret == EBUSY )
|
||||
return false;
|
||||
if( ret )
|
||||
RageException::Throw( "pthread_mutex_lock failed: %s", strerror(ret) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void MutexImpl_Pthreads::Unlock()
|
||||
{
|
||||
@@ -191,6 +212,58 @@ MutexImpl *MakeMutex( RageMutex *pParent )
|
||||
return new MutexImpl_Pthreads( pParent );
|
||||
}
|
||||
|
||||
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( &sem, &ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SemaImpl_Pthreads::Post()
|
||||
{
|
||||
sem_post( &sem );
|
||||
}
|
||||
|
||||
bool SemaImpl_Pthreads::Wait()
|
||||
{
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
CHECKPOINT;
|
||||
ret = sem_wait( &sem );
|
||||
}
|
||||
while( ret == -1 && errno == EINTR );
|
||||
|
||||
ASSERT_M( ret == 0, ssprintf("sem_wait: %s", strerror(errno)) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaImpl_Pthreads::TryWait()
|
||||
{
|
||||
int ret = sem_trywait( &sem );
|
||||
if( ret == EBUSY )
|
||||
return false;
|
||||
if( ret )
|
||||
RageException::Throw( "sem_trywait failed: %s", strerror(ret) );
|
||||
return true;
|
||||
}
|
||||
|
||||
SemaImpl *MakeSemaphore( int iInitialValue )
|
||||
{
|
||||
return new SemaImpl_Pthreads( iInitialValue );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Glenn Maynard
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
class ThreadImpl_Pthreads: public ThreadImpl
|
||||
{
|
||||
public:
|
||||
// HANDLE ThreadHandle;
|
||||
// DWORD ThreadId;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
#if defined(PID_BASED_THREADS)
|
||||
@@ -36,15 +33,32 @@ public:
|
||||
int Wait();
|
||||
};
|
||||
|
||||
struct MutexImpl_Pthreads: public MutexImpl
|
||||
class MutexImpl_Pthreads: public MutexImpl
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
public:
|
||||
MutexImpl_Pthreads( RageMutex *parent );
|
||||
~MutexImpl_Pthreads();
|
||||
|
||||
bool Lock();
|
||||
bool TryLock();
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -94,6 +94,29 @@ MutexImpl_Win32::~MutexImpl_Win32()
|
||||
CloseHandle( mutex );
|
||||
}
|
||||
|
||||
static bool SimpleWaitForSingleObject( HANDLE h, DWORD ms )
|
||||
{
|
||||
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;
|
||||
@@ -102,33 +125,24 @@ bool MutexImpl_Win32::Lock()
|
||||
while( tries-- )
|
||||
{
|
||||
/* Wait for fifteen seconds. If it takes longer than that, we're probably deadlocked. */
|
||||
DWORD ret = WaitForSingleObject( mutex, len );
|
||||
|
||||
switch( ret )
|
||||
{
|
||||
case WAIT_ABANDONED:
|
||||
/* The docs aren't particular about what this does, but it should never happen. */
|
||||
ASSERT( 0 );
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0:
|
||||
if( SimpleWaitForSingleObject( mutex, len ) )
|
||||
return true;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
/* 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 = 1000;
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
FAIL_M( werr_ssprintf(GetLastError(), "WaitForSingleObject(%s)", this->m_Parent->GetName().c_str()) );
|
||||
}
|
||||
/* 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 = 1000;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool MutexImpl_Win32::TryLock()
|
||||
{
|
||||
return SimpleWaitForSingleObject( mutex, 0 );
|
||||
}
|
||||
|
||||
void MutexImpl_Win32::Unlock()
|
||||
{
|
||||
const bool ret = !!ReleaseMutex( mutex );
|
||||
@@ -154,6 +168,60 @@ MutexImpl *MakeMutex( RageMutex *pParent )
|
||||
return new MutexImpl_Win32( pParent );
|
||||
}
|
||||
|
||||
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 = 2;
|
||||
|
||||
while( tries-- )
|
||||
{
|
||||
/* Wait for fifteen 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 one more time, 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.
|
||||
|
||||
@@ -20,15 +20,35 @@ public:
|
||||
int Wait();
|
||||
};
|
||||
|
||||
struct MutexImpl_Win32: public MutexImpl
|
||||
class MutexImpl_Win32: public MutexImpl
|
||||
{
|
||||
HANDLE mutex;
|
||||
|
||||
public:
|
||||
MutexImpl_Win32( RageMutex *parent );
|
||||
~MutexImpl_Win32();
|
||||
|
||||
bool Lock();
|
||||
bool TryLock();
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
HANDLE mutex;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user