diff --git a/stepmania/src/arch/Threads/Threads.h b/stepmania/src/arch/Threads/Threads.h index 60b07c6228..9b2943d8d4 100644 --- a/stepmania/src/arch/Threads/Threads.h +++ b/stepmania/src/arch/Threads/Threads.h @@ -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 diff --git a/stepmania/src/arch/Threads/Threads_Pthreads.cpp b/stepmania/src/arch/Threads/Threads_Pthreads.cpp index 6dca201eba..737ffed5c7 100644 --- a/stepmania/src/arch/Threads/Threads_Pthreads.cpp +++ b/stepmania/src/arch/Threads/Threads_Pthreads.cpp @@ -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 diff --git a/stepmania/src/arch/Threads/Threads_Pthreads.h b/stepmania/src/arch/Threads/Threads_Pthreads.h index 6ba8f75d23..3dd73fc407 100644 --- a/stepmania/src/arch/Threads/Threads_Pthreads.h +++ b/stepmania/src/arch/Threads/Threads_Pthreads.h @@ -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 diff --git a/stepmania/src/arch/Threads/Threads_Win32.cpp b/stepmania/src/arch/Threads/Threads_Win32.cpp index aae90b68ef..0ce4f0f2e2 100644 --- a/stepmania/src/arch/Threads/Threads_Win32.cpp +++ b/stepmania/src/arch/Threads/Threads_Win32.cpp @@ -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. diff --git a/stepmania/src/arch/Threads/Threads_Win32.h b/stepmania/src/arch/Threads/Threads_Win32.h index 3e1d6975c8..38741d8a35 100644 --- a/stepmania/src/arch/Threads/Threads_Win32.h +++ b/stepmania/src/arch/Threads/Threads_Win32.h @@ -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