add TryLock
add semaphores
This commit is contained in:
@@ -7,17 +7,6 @@ class RageMutex;
|
|||||||
|
|
||||||
class ThreadImpl
|
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:
|
public:
|
||||||
virtual ~ThreadImpl() { }
|
virtual ~ThreadImpl() { }
|
||||||
virtual void Halt( bool Kill ) = 0;
|
virtual void Halt( bool Kill ) = 0;
|
||||||
@@ -46,15 +35,30 @@ public:
|
|||||||
* all other errors should fail with an assertion. */
|
* all other errors should fail with an assertion. */
|
||||||
virtual bool Lock() = 0;
|
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
|
/* 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. */
|
* may fail with an assertion if the mutex is not locked. */
|
||||||
virtual void Unlock() = 0;
|
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. */
|
/* These functions must be implemented by the thread implementation. */
|
||||||
ThreadImpl *MakeThread( int (*fn)(void *), void *data );
|
ThreadImpl *MakeThread( int (*fn)(void *), void *data );
|
||||||
ThreadImpl *MakeThisThread();
|
ThreadImpl *MakeThisThread();
|
||||||
MutexImpl *MakeMutex( RageMutex *pParent );
|
MutexImpl *MakeMutex( RageMutex *pParent );
|
||||||
|
SemaImpl *MakeSemaphore( int iInitialValue );
|
||||||
uint64_t GetThisThreadId();
|
uint64_t GetThisThreadId();
|
||||||
|
|
||||||
/* Since ThreadId is implementation-defined, we can't define a universal invalid
|
/* Since ThreadId is implementation-defined, we can't define a universal invalid
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ bool MutexImpl_Pthreads::Lock()
|
|||||||
case 0:
|
case 0:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case EINTR:
|
||||||
|
/* Ignore it. */
|
||||||
|
++tries;
|
||||||
|
continue;
|
||||||
|
|
||||||
case ETIMEDOUT:
|
case ETIMEDOUT:
|
||||||
/* Timed out. Probably deadlocked. Try again one more time, with a smaller
|
/* 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
|
* timeout, just in case we're debugging and happened to stop while waiting
|
||||||
@@ -153,19 +158,35 @@ bool MutexImpl_Pthreads::Lock()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
RageException::Throw( "pthread_mutex_timedlock: %s", strerror(ret) );
|
FAIL_M( ssprintf("pthread_mutex_timedlock: %s", strerror(ret)) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
int ret = pthread_mutex_lock( &mutex );
|
int ret;
|
||||||
if( ret )
|
do
|
||||||
RageException::Throw( "pthread_mutex_lock failed: %s", strerror(ret) );
|
{
|
||||||
|
CHECKPOINT;
|
||||||
|
ret = pthread_mutex_lock( &mutex );
|
||||||
|
}
|
||||||
|
while( ret == -1 && ret == EINTR );
|
||||||
|
|
||||||
|
ASSERT_M( ret == 0, ssprintf("pthread_mutex_lock: %s", strerror(errno)) );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#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()
|
void MutexImpl_Pthreads::Unlock()
|
||||||
{
|
{
|
||||||
@@ -191,6 +212,58 @@ MutexImpl *MakeMutex( RageMutex *pParent )
|
|||||||
return new MutexImpl_Pthreads( 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
|
* (c) 2001-2004 Glenn Maynard
|
||||||
|
|||||||
@@ -10,9 +10,6 @@
|
|||||||
class ThreadImpl_Pthreads: public ThreadImpl
|
class ThreadImpl_Pthreads: public ThreadImpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// HANDLE ThreadHandle;
|
|
||||||
// DWORD ThreadId;
|
|
||||||
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
#if defined(PID_BASED_THREADS)
|
#if defined(PID_BASED_THREADS)
|
||||||
@@ -36,15 +33,32 @@ public:
|
|||||||
int Wait();
|
int Wait();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MutexImpl_Pthreads: public MutexImpl
|
class MutexImpl_Pthreads: public MutexImpl
|
||||||
{
|
{
|
||||||
pthread_mutex_t mutex;
|
public:
|
||||||
|
|
||||||
MutexImpl_Pthreads( RageMutex *parent );
|
MutexImpl_Pthreads( RageMutex *parent );
|
||||||
~MutexImpl_Pthreads();
|
~MutexImpl_Pthreads();
|
||||||
|
|
||||||
bool Lock();
|
bool Lock();
|
||||||
|
bool TryLock();
|
||||||
void Unlock();
|
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
|
#endif
|
||||||
|
|||||||
@@ -94,6 +94,29 @@ MutexImpl_Win32::~MutexImpl_Win32()
|
|||||||
CloseHandle( mutex );
|
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()
|
bool MutexImpl_Win32::Lock()
|
||||||
{
|
{
|
||||||
int len = 15000;
|
int len = 15000;
|
||||||
@@ -102,33 +125,24 @@ bool MutexImpl_Win32::Lock()
|
|||||||
while( tries-- )
|
while( tries-- )
|
||||||
{
|
{
|
||||||
/* Wait for fifteen seconds. If it takes longer than that, we're probably deadlocked. */
|
/* Wait for fifteen seconds. If it takes longer than that, we're probably deadlocked. */
|
||||||
DWORD ret = WaitForSingleObject( mutex, len );
|
if( SimpleWaitForSingleObject( 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:
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case WAIT_TIMEOUT:
|
/* Timed out; probably deadlocked. Try again one more time, with a smaller
|
||||||
/* 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
|
* timeout, just in case we're debugging and happened to stop while waiting
|
||||||
* on the mutex. */
|
* on the mutex. */
|
||||||
len = 1000;
|
len = 1000;
|
||||||
break;
|
|
||||||
|
|
||||||
case WAIT_FAILED:
|
|
||||||
FAIL_M( werr_ssprintf(GetLastError(), "WaitForSingleObject(%s)", this->m_Parent->GetName().c_str()) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MutexImpl_Win32::TryLock()
|
||||||
|
{
|
||||||
|
return SimpleWaitForSingleObject( mutex, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
void MutexImpl_Win32::Unlock()
|
void MutexImpl_Win32::Unlock()
|
||||||
{
|
{
|
||||||
const bool ret = !!ReleaseMutex( mutex );
|
const bool ret = !!ReleaseMutex( mutex );
|
||||||
@@ -154,6 +168,60 @@ MutexImpl *MakeMutex( RageMutex *pParent )
|
|||||||
return new MutexImpl_Win32( 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
|
* (c) 2001-2004 Glenn Maynard
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
|||||||
@@ -20,15 +20,35 @@ public:
|
|||||||
int Wait();
|
int Wait();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MutexImpl_Win32: public MutexImpl
|
class MutexImpl_Win32: public MutexImpl
|
||||||
{
|
{
|
||||||
HANDLE mutex;
|
public:
|
||||||
|
|
||||||
MutexImpl_Win32( RageMutex *parent );
|
MutexImpl_Win32( RageMutex *parent );
|
||||||
~MutexImpl_Win32();
|
~MutexImpl_Win32();
|
||||||
|
|
||||||
bool Lock();
|
bool Lock();
|
||||||
|
bool TryLock();
|
||||||
void Unlock();
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user