diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index 86a20934db..b867c04de7 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -38,15 +38,16 @@ namespace Exception #if wxUSE_GUI - // -------------------------------------------------------------------------------------- - // ThreadTimedOut Exception - // -------------------------------------------------------------------------------------- - // This exception is thrown by Semaphore and Mutex Wait/Lock functions if a blocking wait is - // needed due to gui Yield recursion, and the timeout period for deadlocking (usually 3 seconds) - // is reached before the lock becomes available. This exception cannot occur in the following - // conditions: - // * If the user-specified timeout is less than the deadlock timeout. - // * If the method is run from a thread *other* than the MainGui thread. +// -------------------------------------------------------------------------------------- +// ThreadTimedOut Exception +// -------------------------------------------------------------------------------------- +// This exception is thrown by Semaphore and Mutex Wait/Aquire functions if a blocking wait is +// needed due to gui Yield recursion, and the timeout period for deadlocking (usually 3 seconds) +// is reached before the lock becomes available. This exception cannot occur in the following +// conditions: +// * If the user-specified timeout is less than the deadlock timeout. +// * If the method is run from a thread *other* than the MainGui thread. +// class ThreadTimedOut : public virtual RuntimeError { public: @@ -57,12 +58,11 @@ namespace Exception namespace Threading { - // -------------------------------------------------------------------------------------- - // Platform Specific External APIs - // -------------------------------------------------------------------------------------- - // The following set of documented functions have Linux/Win32 specific implementations, - // which are found in WinThreads.cpp and LnxThreads.cpp - +// -------------------------------------------------------------------------------------- +// Platform Specific External APIs +// -------------------------------------------------------------------------------------- +// The following set of documented functions have Linux/Win32 specific implementations, +// which are found in WinThreads.cpp and LnxThreads.cpp // Returns the number of available logical CPUs (cores plus hyperthreaded cpus) extern void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ); @@ -81,6 +81,31 @@ namespace Threading // sleeps the current thread for the given number of milliseconds. extern void Sleep( int ms ); +// -------------------------------------------------------------------------------------- +// AtomicExchange / AtomicIncrement +// -------------------------------------------------------------------------------------- +// Our fundamental interlocking functions. All other useful interlocks can be derived +// from these little beasties! (these are all implemented internally using cross-platform +// implementations of _InterlockedExchange and such) + + extern u32 AtomicExchange( volatile u32& Target, u32 value ); + extern u32 AtomicExchangeAdd( volatile u32& Target, u32 value ); + extern u32 AtomicIncrement( volatile u32& Target ); + extern u32 AtomicDecrement( volatile u32& Target ); + extern s32 AtomicExchange( volatile s32& Target, s32 value ); + extern s32 AtomicExchangeAdd( volatile s32& Target, u32 value ); + extern s32 AtomicIncrement( volatile s32& Target ); + extern s32 AtomicDecrement( volatile s32& Target ); + + extern void* _AtomicExchangePointer( void * volatile * const target, void* const value ); + extern void* _AtomicCompareExchangePointer( void * volatile * const target, void* const value, void* const comparand ); + +#define AtomicExchangePointer( target, value ) \ + _InterlockedExchangePointer( &target, value ) + +#define AtomicCompareExchangePointer( target, value, comparand ) \ + _InterlockedCompareExchangePointer( &target, value, comparand ) + // pthread Cond is an evil api that is not suited for Pcsx2 needs. // Let's not use it. Use mutexes and semaphores instead to create waits. (Air) @@ -98,36 +123,38 @@ namespace Threading }; #endif - // -------------------------------------------------------------------------------------- - // NonblockingMutex - // -------------------------------------------------------------------------------------- - // This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's - // trylock(), but without any of the extra overhead needed to set up a structure capable - // of blocking waits. It basically optimizes to a single InterlockedExchange. - // - // Simple use: if TryLock() returns false, the Bool is already interlocked by another thread. - // If TryLock() returns true, you've locked the object and are *responsible* for unlocking - // it later. - // +// -------------------------------------------------------------------------------------- +// NonblockingMutex +// -------------------------------------------------------------------------------------- +// This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's +// trylock(), but without any of the extra overhead needed to set up a structure capable +// of blocking waits. It basically optimizes to a single InterlockedExchange. +// +// Simple use: if TryAquire() returns false, the Bool is already interlocked by another thread. +// If TryAquire() returns true, you've locked the object and are *responsible* for unlocking +// it later. +// class NonblockingMutex { protected: - volatile long val; + volatile int val; public: NonblockingMutex() : val( false ) {} virtual ~NonblockingMutex() throw() {} - bool TryLock() throw() + bool TryAquire() throw() { - return !_InterlockedExchange( &val, true ); + return !AtomicExchange( val, true ); } bool IsLocked() { return !!val; } void Release() - { val = false; } + { + AtomicExchange( val, false ); + } }; class Semaphore @@ -153,37 +180,37 @@ namespace Threading bool Wait( const wxTimeSpan& timeout ); }; - class MutexLock + class Mutex { protected: pthread_mutex_t m_mutex; public: - MutexLock(); - virtual ~MutexLock() throw(); + Mutex(); + virtual ~Mutex() throw(); virtual bool IsRecursive() const { return false; } void Recreate(); bool RecreateIfLocked(); void Detach(); - void Lock(); - bool Lock( const wxTimeSpan& timeout ); - bool TryLock(); - void Unlock(); + void Aquire(); + bool Aquire( const wxTimeSpan& timeout ); + bool TryAquire(); + void Release(); - void LockRaw(); - bool LockRaw( const wxTimeSpan& timeout ); + void FullBlockingAquire(); + bool FullBlockingAquire( const wxTimeSpan& timeout ); void Wait(); bool Wait( const wxTimeSpan& timeout ); protected: // empty constructor used by MutexLockRecursive - MutexLock( bool ) {} + Mutex( bool ) {} }; - class MutexLockRecursive : public MutexLock + class MutexLockRecursive : public Mutex { public: MutexLockRecursive(); @@ -253,7 +280,7 @@ namespace Threading pthread_t m_thread; Semaphore m_sem_event; // general wait event that's needed by most threads. - MutexLock m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner + Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally. volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid @@ -275,7 +302,7 @@ namespace Threading virtual void RethrowException() const; void WaitOnSelf( Semaphore& mutex ); - void WaitOnSelf( MutexLock& mutex ); + void WaitOnSelf( Mutex& mutex ); bool IsRunning() const; bool IsSelf() const; @@ -315,7 +342,7 @@ namespace Threading TestCancel(); } - void FrankenMutex( MutexLock& mutex ); + void FrankenMutex( Mutex& mutex ); // ---------------------------------------------------------------------------- // Section of methods for internal use only. @@ -330,46 +357,48 @@ namespace Threading static void _pt_callback_cleanup( void* handle ); }; - ////////////////////////////////////////////////////////////////////////////////////////// - // ScopedLock: Helper class for using Mutexes. - // Using this class provides an exception-safe (and generally clean) method of locking - // code inside a function or conditional block. + // -------------------------------------------------------------------------------------- + // ScopedLock + // -------------------------------------------------------------------------------------- + // Helper class for using Mutexes. Using this class provides an exception-safe (and + // generally clean) method of locking code inside a function or conditional block. The lock + // will be automatically released on any return or exit from the function. // class ScopedLock { DeclareNoncopyableObject(ScopedLock); protected: - MutexLock& m_lock; + Mutex& m_lock; bool m_IsLocked; public: virtual ~ScopedLock() throw() { if( m_IsLocked ) - m_lock.Unlock(); + m_lock.Release(); } - ScopedLock( MutexLock& locker ) : + ScopedLock( Mutex& locker ) : m_lock( locker ) , m_IsLocked( true ) { - m_lock.Lock(); + m_lock.Aquire(); } // Provides manual unlocking of a scoped lock prior to object destruction. - void Unlock() + void Release() { if( !m_IsLocked ) return; m_IsLocked = false; - m_lock.Unlock(); + m_lock.Release(); } // provides manual locking of a scoped lock, to re-lock after a manual unlocking. - void Lock() + void Aquire() { if( m_IsLocked ) return; - m_lock.Lock(); + m_lock.Aquire(); m_IsLocked = true; } @@ -377,9 +406,9 @@ namespace Threading protected: // Special constructor used by ScopedTryLock - ScopedLock( MutexLock& locker, bool isTryLock ) : + ScopedLock( Mutex& locker, bool isTryLock ) : m_lock( locker ) - , m_IsLocked( isTryLock ? m_lock.TryLock() : false ) + , m_IsLocked( isTryLock ? m_lock.TryAquire() : false ) { } @@ -388,51 +417,84 @@ namespace Threading class ScopedTryLock : public ScopedLock { public: - ScopedTryLock( MutexLock& locker ) : ScopedLock( locker, true ) { } + ScopedTryLock( Mutex& locker ) : ScopedLock( locker, true ) { } virtual ~ScopedTryLock() throw() {} bool Failed() const { return !m_IsLocked; } }; - ////////////////////////////////////////////////////////////////////////////////////////// - // BaseTaskThread - an abstract base class which provides simple parallel execution of - // single tasks. - // - // Implementation: - // To use this class your derived class will need to implement its own Task() function - // and also a "StartTask( parameters )" function which suits the need of your task, along - // with any local variables your task needs to do its job. You may additionally want to - // implement a "GetResult()" function, which would be a combination of WaitForResult() - // and a return value of the computational result. - // - // Thread Safety: - // If operating on local variables, you must execute WaitForResult() before leaving the - // variable scope -- or alternatively have your StartTask() implementation make full - // copies of dependent data. Also, by default PostTask() always assumes the previous - // task has completed. If your system can post a new task before the previous one has - // completed, then it needs to explicitly call WaitForResult() or provide a mechanism - // to cancel the previous task (which is probably more work than it's worth). - // - // Performance notes: - // * Remember that thread creation is generally slow, so you should make your object - // instance once early and then feed it tasks repeatedly over the course of program - // execution. - // - // * For threading to be a successful speedup, the task being performed should be as lock - // free as possible. For example using STL containers in parallel usually fails to - // yield any speedup due to the gratuitous amount of locking that the STL performs - // internally. - // - // * The best application of tasking threads is to divide a large loop over a linear array - // into smaller sections. For example, if you have 20,000 items to process, the task - // can be divided into two threads of 10,000 items each. - // +// -------------------------------------------------------------------------------------- +// ScopedNonblockingLock +// -------------------------------------------------------------------------------------- +// A ScopedTryLock branded for use with Nonblocking mutexes. See ScopedTryLock for details. +// + class ScopedNonblockingLock + { + DeclareNoncopyableObject(ScopedNonblockingLock); + + protected: + NonblockingMutex& m_lock; + bool m_IsLocked; + + public: + ScopedNonblockingLock( NonblockingMutex& locker ) : + m_lock( locker ) + , m_IsLocked( m_lock.TryAquire() ) + { + } + + virtual ~ScopedNonblockingLock() throw() + { + if( m_IsLocked ) + m_lock.Release(); + } + + bool Failed() const { return !m_IsLocked; } + }; + +// -------------------------------------------------------------------------------------- +// BaseTaskThread +// -------------------------------------------------------------------------------------- +// an abstract base class which provides simple parallel execution of single tasks. +// +// FIXME: This class is incomplete and untested! Don't use, unless you want to fix it +// while you're at it. :D +// +// Implementation: +// To use this class your derived class will need to implement its own Task() function +// and also a "StartTask( parameters )" function which suits the need of your task, along +// with any local variables your task needs to do its job. You may additionally want to +// implement a "GetResult()" function, which would be a combination of WaitForResult() +// and a return value of the computational result. +// +// Thread Safety: +// If operating on local variables, you must execute WaitForResult() before leaving the +// variable scope -- or alternatively have your StartTask() implementation make full +// copies of dependent data. Also, by default PostTask() always assumes the previous +// task has completed. If your system can post a new task before the previous one has +// completed, then it needs to explicitly call WaitForResult() or provide a mechanism +// to cancel the previous task (which is probably more work than it's worth). +// +// Performance notes: +// * Remember that thread creation is generally slow, so you should make your object +// instance once early and then feed it tasks repeatedly over the course of program +// execution. +// +// * For threading to be a successful speedup, the task being performed should be as lock +// free as possible. For example using STL containers in parallel usually fails to +// yield any speedup due to the gratuitous amount of locking that the STL performs +// internally. +// +// * The best application of tasking threads is to divide a large loop over a linear array +// into smaller sections. For example, if you have 20,000 items to process, the task +// can be divided into two threads of 10,000 items each. +// class BaseTaskThread : public PersistentThread { protected: volatile bool m_Done; volatile bool m_TaskPending; Semaphore m_post_TaskComplete; - MutexLock m_lock_TaskComplete; + Mutex m_lock_TaskComplete; public: virtual ~BaseTaskThread() throw() {} @@ -454,28 +516,5 @@ namespace Threading virtual void ExecuteTaskInThread(); }; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Our fundamental interlocking functions. All other useful interlocks can be derived - // from these little beasties! - - extern u32 AtomicExchange( volatile u32& Target, u32 value ); - extern u32 AtomicExchangeAdd( volatile u32& Target, u32 value ); - extern u32 AtomicIncrement( volatile u32& Target ); - extern u32 AtomicDecrement( volatile u32& Target ); - extern s32 AtomicExchange( volatile s32& Target, s32 value ); - extern s32 AtomicExchangeAdd( volatile s32& Target, u32 value ); - extern s32 AtomicIncrement( volatile s32& Target ); - extern s32 AtomicDecrement( volatile s32& Target ); - - extern void* _AtomicExchangePointer( void * volatile * const target, void* const value ); - extern void* _AtomicCompareExchangePointer( void * volatile * const target, void* const value, void* const comparand ); - - #define AtomicExchangePointer( target, value ) \ - _InterlockedExchangePointer( &target, value ) - - #define AtomicCompareExchangePointer( target, value, comparand ) \ - _InterlockedCompareExchangePointer( &target, value, comparand ) - } diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp index c3c3267727..383691712e 100644 --- a/common/src/Utilities/Console.cpp +++ b/common/src/Utilities/Console.cpp @@ -204,7 +204,7 @@ const IConsoleWriter ConsoleWriter_wxError = static const int MaxFormattedStringLength = 0x80000; template< typename CharType > -class FormatBuffer : public MutexLock +class FormatBuffer : public Mutex { public: bool& clearbit; diff --git a/common/src/Utilities/Mutex.cpp b/common/src/Utilities/Mutex.cpp index a90eadc343..4bf985bf1e 100644 --- a/common/src/Utilities/Mutex.cpp +++ b/common/src/Utilities/Mutex.cpp @@ -27,15 +27,15 @@ namespace Threading } // -------------------------------------------------------------------------------------- -// MutexLock Implementations +// Mutex Implementations // -------------------------------------------------------------------------------------- -Threading::MutexLock::MutexLock() +Threading::Mutex::Mutex() { pthread_mutex_init( &m_mutex, NULL ); } -void Threading::MutexLock::Detach() +void Threading::Mutex::Detach() { if( EBUSY != pthread_mutex_destroy(&m_mutex) ) return; @@ -46,7 +46,7 @@ void Threading::MutexLock::Detach() // (note: if the mutex is locked recursively more than twice then this assert won't // detect it) - Unlock(); Unlock(); // in case of double recursion. + Release(); Release(); // in case of double recursion. int result = pthread_mutex_destroy( &m_mutex ); if( pxAssertDev( result != EBUSY, "Detachment of a recursively-locked mutex (self-locked!)." ) ) return; } @@ -57,14 +57,14 @@ void Threading::MutexLock::Detach() Console.Error( "(Thread Log) Mutex cleanup failed due to possible deadlock."); } -Threading::MutexLock::~MutexLock() throw() +Threading::Mutex::~Mutex() throw() { try { - MutexLock::Detach(); + Mutex::Detach(); } DESTRUCTOR_CATCHALL; } -Threading::MutexLockRecursive::MutexLockRecursive() : MutexLock( false ) +Threading::MutexLockRecursive::MutexLockRecursive() : Mutex( false ) { if( _InterlockedIncrement( &_attr_refcount ) == 1 ) { @@ -88,7 +88,7 @@ Threading::MutexLockRecursive::~MutexLockRecursive() throw() // the application to survive unexpected or inconvenient failures, where a mutex is deadlocked by // a rogue thread. This function allows us to Recreate the mutex and let the deadlocked one ponder // the deeper meanings of the universe for eternity. -void Threading::MutexLock::Recreate() +void Threading::Mutex::Recreate() { Detach(); pthread_mutex_init( &m_mutex, NULL ); @@ -97,7 +97,7 @@ void Threading::MutexLock::Recreate() // Returns: // true if the mutex had to be recreated due to lock contention, or false if the mutex is safely // unlocked. -bool Threading::MutexLock::RecreateIfLocked() +bool Threading::Mutex::RecreateIfLocked() { if( !Wait(def_deadlock_timeout) ) { @@ -108,81 +108,85 @@ bool Threading::MutexLock::RecreateIfLocked() } -void Threading::MutexLock::LockRaw() +// This is a direct blocking action -- very fast, very efficient, and generally very dangerous +// if used from the main GUI thread, since it typically results in an unresponsive program. +// Call this method directly only if you know the code in question will be run from threads +// other than the main thread. +void Threading::Mutex::FullBlockingAquire() { pthread_mutex_lock( &m_mutex ); } -bool Threading::MutexLock::LockRaw( const wxTimeSpan& timeout ) +bool Threading::Mutex::FullBlockingAquire( const wxTimeSpan& timeout ) { wxDateTime megafail( wxDateTime::UNow() + timeout ); const timespec fail = { megafail.GetTicks(), megafail.GetMillisecond() * 1000000 }; return pthread_mutex_timedlock( &m_mutex, &fail ) == 0; } -void Threading::MutexLock::Unlock() +void Threading::Mutex::Release() { pthread_mutex_unlock( &m_mutex ); } -bool Threading::MutexLock::TryLock() +bool Threading::Mutex::TryAquire() { return EBUSY != pthread_mutex_trylock( &m_mutex ); } -// This is a wxApp-safe rendition of LockRaw, which makes sure to execute pending app events +// This is a wxApp-safe rendition of FullBlockingAquire, which makes sure to execute pending app events // and messages *if* the lock is performed from the main GUI thread. // // Exceptions: // ThreadTimedOut - See description of ThreadTimedOut for details // -void Threading::MutexLock::Lock() +void Threading::Mutex::Aquire() { #if wxUSE_GUI if( !wxThread::IsMain() || (wxTheApp == NULL) ) { - LockRaw(); + FullBlockingAquire(); } - else if( _WaitGui_RecursionGuard( "Mutex::Lock" ) ) + else if( _WaitGui_RecursionGuard( "Mutex::Aquire" ) ) { - if( !LockRaw(def_deadlock_timeout) ) + if( !FullBlockingAquire(def_deadlock_timeout) ) throw Exception::ThreadTimedOut(); } else { - while( !LockRaw(def_yieldgui_interval) ) + while( !FullBlockingAquire(def_yieldgui_interval) ) wxTheApp->Yield( true ); } #else - LockRaw(); + FullBlockingAquire(); #endif } // Exceptions: // ThreadTimedOut - See description of ThreadTimedOut for details // -bool Threading::MutexLock::Lock( const wxTimeSpan& timeout ) +bool Threading::Mutex::Aquire( const wxTimeSpan& timeout ) { #if wxUSE_GUI if( !wxThread::IsMain() || (wxTheApp == NULL) ) { - return LockRaw(timeout); + return FullBlockingAquire(timeout); } - else if( _WaitGui_RecursionGuard( "Mutex::Lock(timeout)" ) ) + else if( _WaitGui_RecursionGuard( "Mutex::Aquire(timeout)" ) ) { if( timeout > def_deadlock_timeout ) { - if( LockRaw(def_deadlock_timeout) ) return true; + if( FullBlockingAquire(def_deadlock_timeout) ) return true; throw Exception::ThreadTimedOut(); } - return LockRaw( timeout ); + return FullBlockingAquire( timeout ); } else { wxTimeSpan countdown( (timeout) ); do { - if( LockRaw( def_yieldgui_interval ) ) break; + if( FullBlockingAquire( def_yieldgui_interval ) ) break; wxTheApp->Yield(true); countdown -= def_yieldgui_interval; } while( countdown.GetMilliseconds() > 0 ); @@ -194,7 +198,7 @@ bool Threading::MutexLock::Lock( const wxTimeSpan& timeout ) throw Exception::ThreadTimedOut(); #else - return LockRaw(); + return FullBlockingAquire(); #endif } @@ -203,19 +207,19 @@ bool Threading::MutexLock::Lock( const wxTimeSpan& timeout ) // specific task, and to block until the task is finished (PersistentThread uses it to // determine if the thread is running or completed, for example). // -// Implemented internally as a simple Lock/Unlock pair. +// Implemented internally as a simple Aquire/Release pair. // // Exceptions: // ThreadTimedOut - See description of ThreadTimedOut for details // -void Threading::MutexLock::Wait() +void Threading::Mutex::Wait() { - Lock(); - Unlock(); + Aquire(); + Release(); } // Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked. -// (Implemented internally as a simple Lock/Unlock pair.) +// (Implemented internally as a simple Aquire/Release pair.) // // Returns: // true if the mutex was freed and is in an unlocked state; or false if the wait timed out @@ -224,11 +228,11 @@ void Threading::MutexLock::Wait() // Exceptions: // ThreadTimedOut - See description of ThreadTimedOut for details // -bool Threading::MutexLock::Wait( const wxTimeSpan& timeout ) +bool Threading::Mutex::Wait( const wxTimeSpan& timeout ) { - if( Lock(timeout) ) + if( Aquire(timeout) ) { - Unlock(); + Release(); return true; } return false; diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index f0a5db7852..830dfd242b 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -114,7 +114,7 @@ Threading::PersistentThread::~PersistentThread() throw() DESTRUCTOR_CATCHALL } -void Threading::PersistentThread::FrankenMutex( MutexLock& mutex ) +void Threading::PersistentThread::FrankenMutex( Mutex& mutex ) { if( mutex.RecreateIfLocked() ) { @@ -268,7 +268,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) // Exceptions: // ThreadTimedOut // -void Threading::PersistentThread::WaitOnSelf( MutexLock& mutex ) +void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) { if( !pxAssertDev( !IsSelf(), "WaitOnSelf called from inside the thread (invalid operation!)" ) ) return; @@ -369,7 +369,7 @@ void Threading::PersistentThread::_ThreadCleanup() _try_virtual_invoke( &PersistentThread::OnCleanupInThread ); - m_lock_InThread.Unlock(); + m_lock_InThread.Release(); } wxString Threading::PersistentThread::GetName() const @@ -390,7 +390,7 @@ void Threading::PersistentThread::OnStartInThread() void Threading::PersistentThread::_internal_execute() { - m_lock_InThread.Lock(); + m_lock_InThread.Aquire(); OnStartInThread(); _DoSetThreadName( m_name ); @@ -510,10 +510,10 @@ void Threading::BaseTaskThread::ExecuteTaskInThread() m_sem_event.WaitRaw(); Task(); - m_lock_TaskComplete.Lock(); + m_lock_TaskComplete.Aquire(); m_TaskPending = false; m_post_TaskComplete.Post(); - m_lock_TaskComplete.Unlock(); + m_lock_TaskComplete.Release(); }; return; diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 9b96ed666e..02dc3193b1 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -520,11 +520,11 @@ static void cdvdDetectDisk() void cdvdNewDiskCB() { - if( !Mutex_NewDiskCB.TryLock() ) return; + if( !Mutex_NewDiskCB.TryAquire() ) return; DoCDVDresetDiskTypeCache(); try { cdvdDetectDisk(); } - catch(...) { Mutex_NewDiskCB.Unlock(); } // ensure mutex gets unlocked. + catch(...) { Mutex_NewDiskCB.Release(); } // ensure mutex gets unlocked. } static void mechaDecryptBytes( u32 madr, int size ) diff --git a/pcsx2/GS.h b/pcsx2/GS.h index bbf0b2e431..943a5f3892 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -92,7 +92,7 @@ protected: uint m_WritePos; // cur pos ee thread is writing to Semaphore m_sem_OpenDone; - MutexLock m_lock_RingRestart; + Mutex m_lock_RingRestart; // used to keep multiple threads from sending packets to the ringbuffer concurrently. MutexLockRecursive m_PacketLocker; @@ -116,7 +116,7 @@ protected: uint m_packet_ringpos; // index of the data location in the ringbuffer. #ifdef RINGBUF_DEBUG_STACK - Threading::MutexLock m_lock_Stack; + Threading::Mutex m_lock_Stack; #endif public: diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index c2f3f6b4df..4bc8a46a42 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -272,7 +272,7 @@ void mtgsThreadObject::ExecuteTaskInThread() pxAssert( stackpos == m_RingPos ); prevCmd = tag; ringposStack.pop_back(); - m_lock_Stack.Unlock(); + m_lock_Stack.Release(); #endif switch( tag.command ) @@ -536,7 +536,7 @@ void mtgsThreadObject::SendDataPacket() SetEvent(); } } - //m_PacketLocker.Unlock(); + //m_PacketLocker.Release(); } int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size ) @@ -561,7 +561,7 @@ static u32 ringtx_inf_s[32]; // size - size of the packet data, in smd128's int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ) { - //m_PacketLocker.Lock(); + //m_PacketLocker.Aquire(); #ifdef PCSX2_GSRING_TX_STATS ringtx_s += size; @@ -674,10 +674,10 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s SpinWait(); } - m_lock_RingRestart.Lock(); + m_lock_RingRestart.Aquire(); SendSimplePacket( GS_RINGTYPE_RESTART, 0, 0, 0 ); m_WritePos = writepos = 0; - m_lock_RingRestart.Unlock(); + m_lock_RingRestart.Release(); SetEvent(); // stall until the read position is past the end of our incoming block, @@ -719,7 +719,7 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s #ifdef RINGBUF_DEBUG_STACK m_lock_Stack.Lock(); ringposStack.push_front( writepos ); - m_lock_Stack.Unlock(); + m_lock_Stack.Release(); #endif // Command qword: Low word is the command, and the high word is the packet @@ -738,7 +738,7 @@ __forceinline uint mtgsThreadObject::_PrepForSimplePacket() #ifdef RINGBUF_DEBUG_STACK m_lock_Stack.Lock(); ringposStack.push_front( m_WritePos ); - m_lock_Stack.Unlock(); + m_lock_Stack.Release(); #endif uint future_writepos = m_WritePos+1; diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp index b6ad5588c1..3035c4e37c 100644 --- a/pcsx2/RecoverySystem.cpp +++ b/pcsx2/RecoverySystem.cpp @@ -72,7 +72,7 @@ protected: void OnStart() { - if( !state_buffer_lock.TryLock() ) + if( !state_buffer_lock.TryAquire() ) throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" ); _parent::OnStart(); @@ -429,7 +429,7 @@ void StateCopy_ThawFromMem() void State_ThawFromMem_Blocking() { - if( !state_buffer_lock.TryLock() ) + if( !state_buffer_lock.TryAquire() ) memLoadingState( state_buffer ).FreezeAll(); state_buffer_lock.Release(); diff --git a/pcsx2/System/SysThreadBase.cpp b/pcsx2/System/SysThreadBase.cpp index f9469c1b06..17af13d2c0 100644 --- a/pcsx2/System/SysThreadBase.cpp +++ b/pcsx2/System/SysThreadBase.cpp @@ -189,6 +189,9 @@ void SysThreadBase::Resume() if( IsSelf() ) return; if( m_ExecMode == ExecMode_Opened ) return; + ScopedNonblockingLock resprotect( m_ResumeProtection ); + if( resprotect.Failed() ) return; + ScopedLock locker( m_ExecModeMutex ); // Implementation Note: @@ -203,9 +206,9 @@ void SysThreadBase::Resume() case ExecMode_NoThreadYet: { - static int __Guard = 0; + /*static int __Guard = 0; RecursionGuard guard( __Guard ); - if( guard.IsReentrant() ) return; + if( guard.IsReentrant() ) return;*/ Start(); if( !m_running || (m_ExecMode == ExecMode_NoThreadYet) ) @@ -242,7 +245,7 @@ void SysThreadBase::Resume() void SysThreadBase::OnStartInThread() { - m_RunningLock.Lock(); + m_RunningLock.Aquire(); _parent::OnStartInThread(); m_ResumeEvent.Post(); } @@ -251,7 +254,7 @@ void SysThreadBase::OnCleanupInThread() { m_ExecMode = ExecMode_NoThreadYet; _parent::OnCleanupInThread(); - m_RunningLock.Unlock(); + m_RunningLock.Release(); } void SysThreadBase::OnSuspendInThread() {} @@ -281,7 +284,7 @@ void SysThreadBase::StateCheckInThread() { OnPauseInThread(); m_ExecMode = ExecMode_Paused; - m_RunningLock.Unlock(); + m_RunningLock.Release(); } // fallthrough... @@ -289,7 +292,7 @@ void SysThreadBase::StateCheckInThread() while( m_ExecMode == ExecMode_Paused ) m_ResumeEvent.WaitRaw(); - m_RunningLock.Lock(); + m_RunningLock.Aquire(); OnResumeInThread( false ); break; @@ -298,7 +301,7 @@ void SysThreadBase::StateCheckInThread() { OnSuspendInThread(); m_ExecMode = ExecMode_Closed; - m_RunningLock.Unlock(); + m_RunningLock.Release(); } // fallthrough... @@ -306,7 +309,7 @@ void SysThreadBase::StateCheckInThread() while( m_ExecMode == ExecMode_Closed ) m_ResumeEvent.WaitRaw(); - m_RunningLock.Lock(); + m_RunningLock.Aquire(); OnResumeInThread( true ); break; diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index 65e8369d81..523ebb1a1f 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -99,7 +99,11 @@ protected: // Locked whenever the thread is not in a suspended state (either closed or paused). // Issue a Wait against this mutex for performing actions that require the thread // to be suspended. - MutexLock m_RunningLock; + Mutex m_RunningLock; + + // Protects the thread from re-entrant resume requests while dependent resources are + // being constructed. + NonblockingMutex m_ResumeProtection; public: explicit SysThreadBase(); @@ -124,11 +128,14 @@ public: bool IsClosed() const { return !IsOpen(); } ExecutionMode GetExecutionMode() const { return m_ExecMode; } - MutexLock& ExecutionModeMutex() { return m_ExecModeMutex; } + Mutex& ExecutionModeMutex() { return m_ExecModeMutex; } virtual bool Suspend( bool isBlocking = true ); virtual void Resume(); virtual bool Pause(); + + virtual bool AquireResumeLock() { return m_ResumeProtection.TryAquire(); } + virtual void ReleaseResumeLock() { m_ResumeProtection.Release(); } protected: virtual void OnStart(); diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 0248b86932..265a35efc4 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -49,6 +49,8 @@ void AppCoreThread::Resume() // Thread control (suspend / resume) should only be performed from the main/gui thread. if( !AllowFromMainThreadOnly() ) return; if( m_ExecMode == ExecMode_Opened ) return; + if( m_ResumeProtection.IsLocked() ) return; + if( !pxAssert( g_plugins != NULL ) ) return; if( sys_resume_lock > 0 ) diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index df07998691..bee4e77ded 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -447,6 +447,12 @@ static wxString _sysexec_elf_override; static void _sendmsg_SysExecute() { + if( !CoreThread.AquireResumeLock() ) + { + DbgCon.WriteLn( "(SysExecute) another resume lock or message is already pending; no message posted." ); + return; + } + wxCommandEvent execevt( pxEVT_SysExecute ); execevt.SetInt( _sysexec_cdvdsrc_type ); wxGetApp().AddPendingEvent( execevt ); @@ -464,6 +470,7 @@ void Pcsx2App::SysExecute() { _sysexec_cdvdsrc_type = -1; _sysexec_elf_override = CoreThread.GetElfOverride(); + if( !m_CorePlugins ) { LoadPluginsPassive( OnSysExecuteAfterPlugins ); @@ -488,6 +495,9 @@ void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override _sendmsg_SysExecute(); } +// This message performs actual system execution (as dictated by SysExecute variants). +// It is implemented as a message handler so that it can be triggered in response to +// the completion of other dependent activites, namely loading plugins. void Pcsx2App::OnSysExecute( wxCommandEvent& evt ) { if( sys_resume_lock > 0 ) @@ -502,10 +512,13 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt ) if( evt.GetInt() != -1 ) CoreThread.Reset(); else CoreThread.Suspend(); CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); - if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() ); + if( evt.GetInt() != -1 || (CDVD == NULL) ) + CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() ); if( !CoreThread.HasValidState() ) CoreThread.SetElfOverride( _sysexec_elf_override ); + + CoreThread.ReleaseResumeLock(); CoreThread.Resume(); } diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index c3e6d0f111..a559d45559 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -364,13 +364,13 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text ) if( m_pendingFlushes > 32 && !wxThread::IsMain() ) { ++m_WaitingThreadsForFlush; - lock.Unlock(); + lock.Release(); if( !m_sem_QueueFlushed.Wait( wxTimeSpan( 0,0,0,500 ) ) ) { // Necessary since the main thread could grab the lock and process before // the above function actually returns (gotta love threading!) - lock.Lock(); + lock.Aquire(); if( m_WaitingThreadsForFlush != 0 ) --m_WaitingThreadsForFlush; } else diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index 5f5f0f3075..91a83539ac 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -215,7 +215,7 @@ void __evt_fastcall MainEmuFrame::OnCorePluginStatusChanged( void* obj, PluginEv MainEmuFrame& mframe = *(MainEmuFrame*)obj; if( !pxAssertMsg( mframe.GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return; - mframe.ApplyCoreStatus(); + //mframe.ApplyCoreStatus(); mframe.ApplyPluginStatus(); } @@ -350,9 +350,6 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): m_menuBoot.Append(MenuId_Boot_ELF, _("Run ELF File..."), _("For running raw binaries")); - wxMenu* recentRunMenu = new wxMenu(); - m_menuBoot.Append(MenuId_Boot_Recent, _("Run Recent"), recentRunMenu); - m_menuBoot.AppendSeparator(); m_menuBoot.Append(MenuId_Exit, _("Exit"), _("Closing PCSX2 may be hazardous to your health")); @@ -554,7 +551,7 @@ void PerPluginMenuInfo::Populate( PluginsEnum_t pid ) // Populate options from the plugin here. MyMenu.Append( GetPluginMenuId_Settings(PluginId), _("Plugin Settings..."), - wxsFormat( _("Opens the %s plugin's advanced settings dialog."), tbl_PluginInfo[pid].GetShortname() ) + wxsFormat( _("Opens the %s plugin's advanced settings dialog."), tbl_PluginInfo[pid].GetShortname().c_str() ) ); }