mirror of https://github.com/PCSX2/pcsx2.git
More threading improvements:
* Added TimedLock to MutexLock * Use MutexLock::TimedLock as a replacement for some sloppy semaphore use in SysCoreThread and PersistentThread. * Minor fixes to thread cleanup when exiting the App * Added some extra deadlock protection to the blocking Wait and Lock functions for Mutexes and Semaphores. * Moved MutexLock and Semaphore implementations into their own files. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2022 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
c3cbdaf016
commit
d69f6610e8
|
@ -1,113 +1,115 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="Utilities" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="../../../deps/debug/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/debug/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-g" />
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||
<Add option="-DPCSX2_DEBUG" />
|
||||
<Add option="-DPCSX2_DEVBUILD" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="../../../deps/release/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/release/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=no --cflags`" />
|
||||
<Add option="-DNDEBUG" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../include/Utilities" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
<Target title="Devel">
|
||||
<Option output="../../../deps/devel/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/devel/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||
<Add option="-DPCSX2_DEVBUILD" />
|
||||
<Add option="-DPCSX2_DEVEL" />
|
||||
<Add option="-DNDEBUG" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../include/Utilities" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add directory="../../include/Utilities" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../../3rdparty" />
|
||||
</Compiler>
|
||||
<Unit filename="../../../3rdparty/google/dense_hash_map" />
|
||||
<Unit filename="../../../3rdparty/google/dense_hash_set" />
|
||||
<Unit filename="../../../3rdparty/google/sparse_hash_map" />
|
||||
<Unit filename="../../../3rdparty/google/sparse_hash_set" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/densehashtable.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/sparseconfig.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/sparsehashtable.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsetable" />
|
||||
<Unit filename="../../../3rdparty/google/type_traits.h" />
|
||||
<Unit filename="../../include/Utilities/Console.h" />
|
||||
<Unit filename="../../include/Utilities/Dependencies.h" />
|
||||
<Unit filename="../../include/Utilities/Exceptions.h" />
|
||||
<Unit filename="../../include/Utilities/General.h" />
|
||||
<Unit filename="../../include/Utilities/HashMap.h" />
|
||||
<Unit filename="../../include/Utilities/Listeners.h" />
|
||||
<Unit filename="../../include/Utilities/MemcpyFast.h" />
|
||||
<Unit filename="../../include/Utilities/Path.h" />
|
||||
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
|
||||
<Unit filename="../../include/Utilities/SafeArray.h" />
|
||||
<Unit filename="../../include/Utilities/ScopedPtr.h" />
|
||||
<Unit filename="../../include/Utilities/StringHelpers.h" />
|
||||
<Unit filename="../../include/Utilities/Threading.h" />
|
||||
<Unit filename="../../include/Utilities/lnx_memzero.h" />
|
||||
<Unit filename="../../include/Utilities/wxBaseTools.h" />
|
||||
<Unit filename="../../include/Utilities/wxGuiTools.h" />
|
||||
<Unit filename="../../include/intrin_x86.h" />
|
||||
<Unit filename="../../src/Utilities/AlignedMalloc.cpp" />
|
||||
<Unit filename="../../src/Utilities/Console.cpp" />
|
||||
<Unit filename="../../src/Utilities/Exceptions.cpp" />
|
||||
<Unit filename="../../src/Utilities/HashTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxHostSys.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxMisc.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxThreads.cpp" />
|
||||
<Unit filename="../../src/Utilities/PathUtils.cpp" />
|
||||
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
|
||||
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
|
||||
<Unit filename="../../src/Utilities/ThreadTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/vssprintf.cpp" />
|
||||
<Unit filename="../../src/Utilities/wxGuiTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/x86/MemcpyFast.S" />
|
||||
<Extensions>
|
||||
<envvars />
|
||||
<code_completion>
|
||||
<search_path add="/usr/include/wx-2.8" />
|
||||
</code_completion>
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="Utilities" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="../../../deps/debug/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/debug/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-g" />
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||
<Add option="-DPCSX2_DEBUG" />
|
||||
<Add option="-DPCSX2_DEVBUILD" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="../../../deps/release/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/release/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=no --cflags`" />
|
||||
<Add option="-DNDEBUG" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../include/Utilities" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
<Target title="Devel">
|
||||
<Option output="../../../deps/devel/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="./.objs/devel/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||
<Add option="-DPCSX2_DEVBUILD" />
|
||||
<Add option="-DPCSX2_DEVEL" />
|
||||
<Add option="-DNDEBUG" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../include/Utilities" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add directory="../../include/Utilities" />
|
||||
<Add directory="../../include" />
|
||||
<Add directory="../../../3rdparty" />
|
||||
</Compiler>
|
||||
<Unit filename="../../../3rdparty/google/dense_hash_map" />
|
||||
<Unit filename="../../../3rdparty/google/dense_hash_set" />
|
||||
<Unit filename="../../../3rdparty/google/sparse_hash_map" />
|
||||
<Unit filename="../../../3rdparty/google/sparse_hash_set" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/densehashtable.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/sparseconfig.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsehash/sparsehashtable.h" />
|
||||
<Unit filename="../../../3rdparty/google/sparsetable" />
|
||||
<Unit filename="../../../3rdparty/google/type_traits.h" />
|
||||
<Unit filename="../../include/Utilities/Console.h" />
|
||||
<Unit filename="../../include/Utilities/Dependencies.h" />
|
||||
<Unit filename="../../include/Utilities/Exceptions.h" />
|
||||
<Unit filename="../../include/Utilities/General.h" />
|
||||
<Unit filename="../../include/Utilities/HashMap.h" />
|
||||
<Unit filename="../../include/Utilities/Listeners.h" />
|
||||
<Unit filename="../../include/Utilities/MemcpyFast.h" />
|
||||
<Unit filename="../../include/Utilities/Path.h" />
|
||||
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
|
||||
<Unit filename="../../include/Utilities/SafeArray.h" />
|
||||
<Unit filename="../../include/Utilities/ScopedPtr.h" />
|
||||
<Unit filename="../../include/Utilities/StringHelpers.h" />
|
||||
<Unit filename="../../include/Utilities/Threading.h" />
|
||||
<Unit filename="../../include/Utilities/lnx_memzero.h" />
|
||||
<Unit filename="../../include/Utilities/wxBaseTools.h" />
|
||||
<Unit filename="../../include/Utilities/wxGuiTools.h" />
|
||||
<Unit filename="../../include/intrin_x86.h" />
|
||||
<Unit filename="../../src/Utilities/AlignedMalloc.cpp" />
|
||||
<Unit filename="../../src/Utilities/Console.cpp" />
|
||||
<Unit filename="../../src/Utilities/Exceptions.cpp" />
|
||||
<Unit filename="../../src/Utilities/HashTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxHostSys.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxMisc.cpp" />
|
||||
<Unit filename="../../src/Utilities/Linux/LnxThreads.cpp" />
|
||||
<Unit filename="../../src/Utilities/Mutex.cpp" />
|
||||
<Unit filename="../../src/Utilities/PathUtils.cpp" />
|
||||
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
|
||||
<Unit filename="../../src/Utilities/Semaphore.cpp" />
|
||||
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
|
||||
<Unit filename="../../src/Utilities/ThreadTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/vssprintf.cpp" />
|
||||
<Unit filename="../../src/Utilities/wxGuiTools.cpp" />
|
||||
<Unit filename="../../src/Utilities/x86/MemcpyFast.S" />
|
||||
<Extensions>
|
||||
<envvars />
|
||||
<code_completion>
|
||||
<search_path add="/usr/include/wx-2.8" />
|
||||
</code_completion>
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
|
|
|
@ -215,6 +215,10 @@
|
|||
RelativePath="..\..\src\Utilities\x86\MemcpyFast.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\Mutex.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\PathUtils.cpp"
|
||||
>
|
||||
|
@ -247,10 +251,18 @@
|
|||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\Semaphore.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\StringHelpers.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\ThreadingInternal.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\ThreadTools.cpp"
|
||||
>
|
||||
|
|
|
@ -279,7 +279,7 @@ namespace Exception
|
|||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Hardware/OS Exceptions:
|
||||
// HardwareDeficiency / CpuStateShutdown / PluginFailure / ThreadCreationError
|
||||
// HardwareDeficiency
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
class HardwareDeficiency : public virtual RuntimeError
|
||||
|
@ -288,18 +288,6 @@ namespace Exception
|
|||
DEFINE_RUNTIME_EXCEPTION( HardwareDeficiency, wxLt("Your machine's hardware is incapable of running Pcsx2. Sorry dood.") );
|
||||
};
|
||||
|
||||
class ThreadCreationError : public virtual RuntimeError
|
||||
{
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( ThreadCreationError, wxLt("Thread could not be created.") );
|
||||
};
|
||||
|
||||
class ThreadTimedOut : public virtual RuntimeError
|
||||
{
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( ThreadTimedOut, "Blocking action timed out due to potential deadlock." );
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Streaming (file) Exceptions:
|
||||
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
|
||||
|
|
|
@ -28,6 +28,33 @@ class wxTimeSpan;
|
|||
#define AllowFromMainThreadOnly() \
|
||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
||||
|
||||
namespace Exception
|
||||
{
|
||||
class ThreadCreationError : public virtual RuntimeError
|
||||
{
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( ThreadCreationError, wxLt("Thread could not be created.") );
|
||||
};
|
||||
|
||||
#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.
|
||||
class ThreadTimedOut : public virtual RuntimeError
|
||||
{
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( ThreadTimedOut, "Blocking action timed out due to potential deadlock." );
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -110,33 +137,40 @@ namespace Threading
|
|||
void Reset();
|
||||
void Post();
|
||||
void Post( int multiple );
|
||||
|
||||
|
||||
void WaitRaw();
|
||||
bool WaitRaw( const wxTimeSpan& timeout );
|
||||
void WaitNoCancel();
|
||||
int Count();
|
||||
|
||||
#if wxUSE_GUI
|
||||
void Wait();
|
||||
bool Wait( const wxTimeSpan& timeout );
|
||||
|
||||
protected:
|
||||
bool _WaitGui_RecursionGuard();
|
||||
#endif
|
||||
};
|
||||
|
||||
class MutexLock
|
||||
{
|
||||
protected:
|
||||
pthread_mutex_t mutex;
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
public:
|
||||
MutexLock();
|
||||
virtual ~MutexLock() throw();
|
||||
virtual bool IsRecursive() const { return false; }
|
||||
|
||||
void Recreate();
|
||||
bool RecreateIfLocked();
|
||||
void Detach();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
bool Lock( const wxTimeSpan& timeout );
|
||||
bool TryLock();
|
||||
void Unlock();
|
||||
|
||||
void LockRaw();
|
||||
bool LockRaw( const wxTimeSpan& timeout );
|
||||
|
||||
void Wait();
|
||||
bool Wait( const wxTimeSpan& timeout );
|
||||
|
||||
protected:
|
||||
// empty constructor used by MutexLockRecursive
|
||||
|
@ -148,6 +182,7 @@ namespace Threading
|
|||
public:
|
||||
MutexLockRecursive();
|
||||
virtual ~MutexLockRecursive() throw();
|
||||
virtual bool IsRecursive() const { return true; }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -212,7 +247,7 @@ namespace Threading
|
|||
|
||||
pthread_t m_thread;
|
||||
Semaphore m_sem_event; // general wait event that's needed by most threads.
|
||||
Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner
|
||||
MutexLock 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
|
||||
|
@ -221,7 +256,7 @@ namespace Threading
|
|||
// exception handle, set non-NULL if the thread terminated with an exception
|
||||
// Use RethrowException() to re-throw the exception using its original exception type.
|
||||
ScopedPtr<Exception::BaseException> m_except;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~PersistentThread() throw();
|
||||
PersistentThread();
|
||||
|
@ -247,10 +282,6 @@ namespace Threading
|
|||
// Implemented by derived class to handle threading actions!
|
||||
virtual void ExecuteTaskInThread()=0;
|
||||
|
||||
// Inserts a thread cancellation point. If the thread has received a cancel request, this
|
||||
// function will throw an SEH exception designed to exit the thread (so make sure to use C++
|
||||
// object encapsulation for anything that could leak resources, to ensure object unwinding
|
||||
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
|
||||
void TestCancel();
|
||||
|
||||
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
|
||||
|
@ -269,6 +300,8 @@ namespace Threading
|
|||
TestCancel();
|
||||
}
|
||||
|
||||
void FrankenMutex( MutexLock& mutex );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Section of methods for internal use only.
|
||||
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Threading.h"
|
||||
#include "wxBaseTools.h"
|
||||
#include "ThreadingInternal.h"
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
static long _attr_refcount = 0;
|
||||
static pthread_mutexattr_t _attr_recursive;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MutexLock Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Threading::MutexLock::MutexLock()
|
||||
{
|
||||
int err = pthread_mutex_init( &m_mutex, NULL );
|
||||
}
|
||||
|
||||
void Threading::MutexLock::Detach()
|
||||
{
|
||||
if( EBUSY != pthread_mutex_destroy(&m_mutex) ) return;
|
||||
|
||||
if( IsRecursive() )
|
||||
{
|
||||
// Sanity check: Recursive locks could be held by our own thread, which would
|
||||
// be considered an assertion failure, but can also be handled gracefully.
|
||||
// (note: if the mutex is locked recursively more than twice then this assert won't
|
||||
// detect it)
|
||||
|
||||
Unlock(); Unlock(); // 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;
|
||||
}
|
||||
|
||||
if( Wait(def_deadlock_timeout) )
|
||||
pthread_mutex_destroy( &m_mutex );
|
||||
else
|
||||
Console.Error( "(Thread Log) Mutex cleanup failed due to possible deadlock.");
|
||||
}
|
||||
|
||||
Threading::MutexLock::~MutexLock() throw()
|
||||
{
|
||||
try {
|
||||
MutexLock::Detach();
|
||||
} DESTRUCTOR_CATCHALL;
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::MutexLockRecursive() : MutexLock( false )
|
||||
{
|
||||
if( _InterlockedIncrement( &_attr_refcount ) == 1 )
|
||||
{
|
||||
if( 0 != pthread_mutexattr_init( &_attr_recursive ) )
|
||||
throw Exception::OutOfMemory( "Out of memory error initializing the Mutex attributes for recursive mutexing." );
|
||||
|
||||
pthread_mutexattr_settype( &_attr_recursive, PTHREAD_MUTEX_RECURSIVE );
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
err = pthread_mutex_init( &m_mutex, &_attr_recursive );
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::~MutexLockRecursive() throw()
|
||||
{
|
||||
if( _InterlockedDecrement( &_attr_refcount ) == 0 )
|
||||
pthread_mutexattr_destroy( &_attr_recursive );
|
||||
}
|
||||
|
||||
// This is a bit of a hackish function, which is technically unsafe, but can be useful for allowing
|
||||
// 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()
|
||||
{
|
||||
Detach();
|
||||
pthread_mutex_init( &m_mutex, NULL );
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
if( !Wait(def_deadlock_timeout) )
|
||||
{
|
||||
Recreate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Threading::MutexLock::LockRaw()
|
||||
{
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
}
|
||||
|
||||
bool Threading::MutexLock::LockRaw( 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()
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
}
|
||||
|
||||
bool Threading::MutexLock::TryLock()
|
||||
{
|
||||
return EBUSY != pthread_mutex_trylock( &m_mutex );
|
||||
}
|
||||
|
||||
// This is a wxApp-safe rendition of LockRaw, 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()
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
LockRaw();
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard( "Mutex::Lock" ) )
|
||||
{
|
||||
if( !LockRaw(def_deadlock_timeout) )
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
wxTheApp->Yield( true );
|
||||
} while( !LockRaw(def_yieldgui_interval) );
|
||||
}
|
||||
#else
|
||||
LockRaw();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - See description of ThreadTimedOut for details
|
||||
//
|
||||
bool Threading::MutexLock::Lock( const wxTimeSpan& timeout )
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
return LockRaw(timeout);
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard( "Mutex::Lock(timeout)" ) )
|
||||
{
|
||||
if( timeout > def_deadlock_timeout )
|
||||
{
|
||||
if( LockRaw(def_deadlock_timeout) ) return true;
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
return LockRaw( timeout );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxTimeSpan countdown( (timeout) );
|
||||
|
||||
do {
|
||||
wxTheApp->Yield(true);
|
||||
if( LockRaw( def_yieldgui_interval ) ) break;
|
||||
countdown -= def_yieldgui_interval;
|
||||
} while( countdown.GetMilliseconds() > 0 );
|
||||
|
||||
return countdown.GetMilliseconds() > 0;
|
||||
}
|
||||
|
||||
// Looks like a potential deadlock; throw an exception!
|
||||
throw Exception::ThreadTimedOut();
|
||||
|
||||
#else
|
||||
return LockRaw();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked.
|
||||
// Typically this action is used to determine if a thread is currently performing some
|
||||
// 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.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - See description of ThreadTimedOut for details
|
||||
//
|
||||
void Threading::MutexLock::Wait()
|
||||
{
|
||||
Lock();
|
||||
Unlock();
|
||||
}
|
||||
|
||||
// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked.
|
||||
// (Implemented internally as a simple Lock/Unlock pair.)
|
||||
//
|
||||
// Returns:
|
||||
// true if the mutex was freed and is in an unlocked state; or false if the wait timed out
|
||||
// and the mutex is still locked by another thread.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - See description of ThreadTimedOut for details
|
||||
//
|
||||
bool Threading::MutexLock::Wait( const wxTimeSpan& timeout )
|
||||
{
|
||||
if( Lock(timeout) )
|
||||
{
|
||||
Unlock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Threading.h"
|
||||
#include "wxBaseTools.h"
|
||||
#include "ThreadingInternal.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Semaphore Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Threading::Semaphore::Semaphore()
|
||||
{
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
Threading::Semaphore::~Semaphore() throw()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Reset()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Post()
|
||||
{
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Post( int multiple )
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
sem_post_multiple( &m_sema, multiple );
|
||||
#else
|
||||
// Only w32pthreads has the post_multiple, but it's easy enough to fake:
|
||||
while( multiple > 0 )
|
||||
{
|
||||
multiple--;
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Threading::Semaphore::WaitRaw()
|
||||
{
|
||||
sem_wait( &m_sema );
|
||||
}
|
||||
|
||||
bool Threading::Semaphore::WaitRaw( const wxTimeSpan& timeout )
|
||||
{
|
||||
wxDateTime megafail( wxDateTime::UNow() + timeout );
|
||||
const timespec fail = { megafail.GetTicks(), megafail.GetMillisecond() * 1000000 };
|
||||
return sem_timedwait( &m_sema, &fail ) != -1;
|
||||
}
|
||||
|
||||
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windoes continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - See description of ThreadTimedOut for details
|
||||
//
|
||||
void Threading::Semaphore::Wait()
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
WaitRaw();
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard( "Semaphore::Wait" ) )
|
||||
{
|
||||
if( !WaitRaw(def_yieldgui_interval) ) // default is 4 seconds
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
wxTheApp->Yield( true );
|
||||
} while( !WaitRaw( def_yieldgui_interval ) );
|
||||
}
|
||||
#else
|
||||
WaitRaw();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is a wxApp-safe implementation of WaitRaw, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windows continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Returns:
|
||||
// false if the wait timed out before the semaphore was signaled, or true if the signal was
|
||||
// reached prior to timeout.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - See description of ThreadTimedOut for details
|
||||
//
|
||||
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard( "Semaphore::Wait(timeout)" ) )
|
||||
{
|
||||
if( timeout > def_deadlock_timeout )
|
||||
{
|
||||
if( WaitRaw(def_deadlock_timeout) ) return true;
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxTimeSpan countdown( (timeout) );
|
||||
|
||||
do {
|
||||
wxTheApp->Yield(true);
|
||||
if( WaitRaw( def_yieldgui_interval ) ) break;
|
||||
countdown -= def_yieldgui_interval;
|
||||
} while( countdown.GetMilliseconds() > 0 );
|
||||
|
||||
return countdown.GetMilliseconds() > 0;
|
||||
}
|
||||
#else
|
||||
return WaitRaw( timeout );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state
|
||||
// after the wait has completed. Useful for situations where the semaphore itself is stored on
|
||||
// the stack and passed to another thread via GUI message or such, avoiding complications where
|
||||
// the thread might be canceled and the stack value becomes invalid.
|
||||
//
|
||||
// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitRaw(), so
|
||||
// consider manually specifying the thread as uncancellable and using WaitRaw() instead if you need
|
||||
// to do a lot of no-cancel waits in a tight loop worker thread, for example.
|
||||
void Threading::Semaphore::WaitNoCancel()
|
||||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
WaitRaw();
|
||||
pthread_setcancelstate( oldstate, NULL );
|
||||
}
|
||||
|
||||
int Threading::Semaphore::Count()
|
||||
{
|
||||
int retval;
|
||||
sem_getvalue( &m_sema, &retval );
|
||||
return retval;
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
#ifdef _WIN32
|
||||
# include <wx/msw/wrapwin.h> // for thread renaming features
|
||||
#endif
|
||||
#include <wx/app.h>
|
||||
|
||||
#ifdef __LINUX__
|
||||
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
||||
|
@ -27,21 +26,35 @@
|
|||
|
||||
#include "Threading.h"
|
||||
#include "wxBaseTools.h"
|
||||
#include "ThreadingInternal.h"
|
||||
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/thread.h>
|
||||
// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread,
|
||||
// to avoid gui deadlock).
|
||||
const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
|
||||
|
||||
namespace Threading
|
||||
// three second interval for deadlock protection on waitgui.
|
||||
const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 );
|
||||
|
||||
// (intended for internal use only)
|
||||
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
|
||||
// handled via normal yielding methods.
|
||||
bool Threading::_WaitGui_RecursionGuard( const char* guardname )
|
||||
{
|
||||
// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread,
|
||||
// to avoid gui deadlock).
|
||||
static const wxTimeSpan ts_waitgui_interval( 0, 0, 0, 100 );
|
||||
// In order to avoid deadlock we need to make sure we cut some time to handle messages.
|
||||
// But this can result in recursive yield calls, which would crash the app. Protect
|
||||
// against them here and, if recursion is detected, perform a standard blocking wait.
|
||||
// (also, wx ignores message pumping on recursive Yields, so no point in allowing
|
||||
// more then one recursion)
|
||||
|
||||
// Four second interval for deadlock protection on waitgui.
|
||||
static const wxTimeSpan ts_waitgui_deadlock( 0, 0, 4, 0 );
|
||||
static int __Guard = 0;
|
||||
RecursionGuard guard( __Guard );
|
||||
|
||||
static long _attr_refcount = 0;
|
||||
static pthread_mutexattr_t _attr_recursive;
|
||||
if( guard.Counter >= 2 )
|
||||
{
|
||||
Console.WriteLn( "(Thread Log) Possible yield recursion detected in %s; performing blocking wait.", guardname );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__forceinline void Threading::Timeslice()
|
||||
|
@ -58,7 +71,7 @@ Threading::PersistentThread::PersistentThread() :
|
|||
m_name( L"PersistentThread" )
|
||||
, m_thread()
|
||||
, m_sem_event()
|
||||
, m_sem_finished()
|
||||
, m_lock_InThread()
|
||||
, m_lock_start()
|
||||
, m_detached( true ) // start out with m_thread in detached/invalid state
|
||||
, m_running( false )
|
||||
|
@ -81,14 +94,7 @@ Threading::PersistentThread::~PersistentThread() throw()
|
|||
if( m_running )
|
||||
{
|
||||
DevCon.WriteLn( L"\tWaiting for running thread to end...");
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.Wait();
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
// Need to lock here so that the thread can finish shutting down before
|
||||
// it gets destroyed, otherwise th mutex handle would become invalid.
|
||||
ScopedLock locker( m_lock_start );
|
||||
m_lock_InThread.Wait();
|
||||
}
|
||||
Threading::Sleep( 1 );
|
||||
Detach();
|
||||
|
@ -110,6 +116,19 @@ Threading::PersistentThread::~PersistentThread() throw()
|
|||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
|
||||
void Threading::PersistentThread::FrankenMutex( MutexLock& mutex )
|
||||
{
|
||||
if( mutex.RecreateIfLocked() )
|
||||
{
|
||||
// Our lock is bupkis, which means the previous thread probably deadlocked.
|
||||
// Let's create a new mutex lock to replace it.
|
||||
|
||||
Console.Error( wxsFormat(
|
||||
L"(Thread Log) Possible deadlock detected on restarted mutex belonging to '%s'.", m_name.c_str() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Main entry point for starting or e-starting a persistent thread. This function performs necessary
|
||||
// locks and checks for avoiding race conditions, and then calls OnStart() immeediately before
|
||||
// the actual thread creation. Extending classes should generally not override Start(), and should
|
||||
|
@ -118,11 +137,13 @@ Threading::PersistentThread::~PersistentThread() throw()
|
|||
// This function should not be called from the owner thread.
|
||||
void Threading::PersistentThread::Start()
|
||||
{
|
||||
ScopedLock startlock( m_lock_start ); // Prevents sudden parallel startup
|
||||
// Prevents sudden parallel startup, and or parallel startup + cancel:
|
||||
ScopedLock startlock( m_lock_start );
|
||||
if( m_running ) return;
|
||||
|
||||
Detach(); // clean up previous thread handle, if one exists.
|
||||
m_sem_finished.Reset();
|
||||
|
||||
FrankenMutex( m_lock_InThread );
|
||||
|
||||
OnStart();
|
||||
|
||||
|
@ -159,23 +180,25 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
|
|||
{
|
||||
pxAssertMsg( !IsSelf(), "Thread affinity error." );
|
||||
|
||||
if( !m_running ) return;
|
||||
|
||||
if( m_detached )
|
||||
{
|
||||
Console.Notice( "(Thread Warning) Ignoring attempted cancelation of detached thread." );
|
||||
return;
|
||||
}
|
||||
// Prevent simultaneous startup and cancel:
|
||||
ScopedLock startlock( m_lock_start );
|
||||
if( !m_running ) return;
|
||||
|
||||
pthread_cancel( m_thread );
|
||||
if( m_detached )
|
||||
{
|
||||
Console.Notice( "(Thread Warning) Ignoring attempted cancellation of detached thread." );
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_cancel( m_thread );
|
||||
|
||||
}
|
||||
|
||||
if( isBlocking )
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.Wait();
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
m_lock_InThread.Wait();
|
||||
Detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,12 +214,7 @@ void Threading::PersistentThread::Block()
|
|||
{
|
||||
pxAssertDev( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
||||
|
||||
if( m_running )
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.Wait();
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
m_lock_InThread.Wait();
|
||||
}
|
||||
|
||||
bool Threading::PersistentThread::IsSelf() const
|
||||
|
@ -218,6 +236,10 @@ void Threading::PersistentThread::RethrowException() const
|
|||
m_except->Rethrow();
|
||||
}
|
||||
|
||||
// Inserts a thread cancellation point. If the thread has received a cancel request, this
|
||||
// function will throw an SEH exception designed to exit the thread (so make sure to use C++
|
||||
// object encapsulation for anything that could leak resources, to ensure object unwinding
|
||||
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
|
||||
void Threading::PersistentThread::TestCancel()
|
||||
{
|
||||
pxAssert( IsSelf() );
|
||||
|
@ -297,15 +319,10 @@ void Threading::PersistentThread::_ThreadCleanup()
|
|||
{
|
||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
||||
|
||||
// Typically thread cleanup needs to lock against thread startup, since both
|
||||
// will perform some measure of variable inits or resets, depending on how the
|
||||
// derived class is implemented.
|
||||
ScopedLock startlock( m_lock_start );
|
||||
|
||||
_try_virtual_invoke( &PersistentThread::OnCleanupInThread );
|
||||
|
||||
m_running = false;
|
||||
m_sem_finished.Post();
|
||||
m_lock_InThread.Unlock();
|
||||
}
|
||||
|
||||
wxString Threading::PersistentThread::GetName() const
|
||||
|
@ -315,6 +332,7 @@ wxString Threading::PersistentThread::GetName() const
|
|||
|
||||
void Threading::PersistentThread::_internal_execute()
|
||||
{
|
||||
m_lock_InThread.Lock();
|
||||
m_running = true;
|
||||
_DoSetThreadName( m_name );
|
||||
_try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
|
||||
|
@ -472,228 +490,6 @@ void Threading::WaitEvent::Wait()
|
|||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Semaphore Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Threading::Semaphore::Semaphore()
|
||||
{
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
Threading::Semaphore::~Semaphore() throw()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Reset()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Post()
|
||||
{
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
|
||||
void Threading::Semaphore::Post( int multiple )
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
sem_post_multiple( &m_sema, multiple );
|
||||
#else
|
||||
// Only w32pthreads has the post_multiple, but it's easy enough to fake:
|
||||
while( multiple > 0 )
|
||||
{
|
||||
multiple--;
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if wxUSE_GUI
|
||||
// (intended for internal use only)
|
||||
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
|
||||
// handled via normal yielding methods.
|
||||
bool Threading::Semaphore::_WaitGui_RecursionGuard()
|
||||
{
|
||||
// In order to avoid deadlock we need to make sure we cut some time to handle
|
||||
// messages. But this can result in recursive yield calls, which would crash
|
||||
// the app. Protect against them here and, if recursion is detected, perform
|
||||
// a standard blocking wait.
|
||||
|
||||
static int __Guard = 0;
|
||||
RecursionGuard guard( __Guard );
|
||||
|
||||
if( guard.Counter > 4 )
|
||||
{
|
||||
Console.WriteLn( "(Thread Log) Possible yield recursion detected in Semaphore::Wait; performing blocking wait." );
|
||||
//while( wxTheApp->Pending() ) wxTheApp->Dispatch(); // ensures console gets updated.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windoes continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - thrown if a blocking wait was needed due to recursion and the default
|
||||
// timeout period (usually 4 seconds) was reached, indicating likely deadlock. If
|
||||
// the method is run from a thread *other* than the MainGui thread, this exception
|
||||
// cannot occur.
|
||||
//
|
||||
void Threading::Semaphore::Wait()
|
||||
{
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
WaitRaw();
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard() )
|
||||
{
|
||||
if( !WaitRaw(ts_waitgui_deadlock) ) // default is 4 seconds
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
wxTheApp->Yield( true );
|
||||
} while( !WaitRaw( ts_waitgui_interval ) );
|
||||
}
|
||||
}
|
||||
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windows continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Returns:
|
||||
// false if the wait timed out before the semaphore was signaled, or true if the signal was
|
||||
// reached prior to timeout.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - thrown if a blocking wait was needed due to recursion and the default
|
||||
// timeout period (usually 4 seconds) was reached, indicating likely deadlock. If the
|
||||
// user-specified timeout is less than four seconds, this exception cannot occur. If
|
||||
// the method is run from a thread *other* than the MainGui thread, this exception
|
||||
// cannot occur.
|
||||
//
|
||||
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
|
||||
{
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard() )
|
||||
{
|
||||
if( timeout > ts_waitgui_deadlock )
|
||||
{
|
||||
if( WaitRaw(ts_waitgui_deadlock) ) return true;
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxTimeSpan countdown( (timeout) );
|
||||
|
||||
do {
|
||||
wxTheApp->Yield();
|
||||
if( WaitRaw( ts_waitgui_interval ) ) break;
|
||||
countdown -= ts_waitgui_interval;
|
||||
} while( countdown.GetMilliseconds() > 0 );
|
||||
|
||||
return countdown.GetMilliseconds() > 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Threading::Semaphore::WaitRaw()
|
||||
{
|
||||
sem_wait( &m_sema );
|
||||
}
|
||||
|
||||
bool Threading::Semaphore::WaitRaw( const wxTimeSpan& timeout )
|
||||
{
|
||||
wxDateTime megafail( wxDateTime::UNow() + timeout );
|
||||
const timespec fail = { megafail.GetTicks(), megafail.GetMillisecond() * 1000000 };
|
||||
return sem_timedwait( &m_sema, &fail ) != -1;
|
||||
}
|
||||
|
||||
// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state
|
||||
// after the wait has completed. Useful for situations where the semaphore itself is stored on
|
||||
// the stack and passed to another thread via GUI message or such, avoiding complications where
|
||||
// the thread might be canceled and the stack value becomes invalid.
|
||||
//
|
||||
// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitRaw(), so
|
||||
// consider manually specifying the thread as uncancellable and using WaitRaw() instead if you need
|
||||
// to do a lot of no-cancel waits in a tight loop worker thread, for example.
|
||||
void Threading::Semaphore::WaitNoCancel()
|
||||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
WaitRaw();
|
||||
pthread_setcancelstate( oldstate, NULL );
|
||||
}
|
||||
|
||||
int Threading::Semaphore::Count()
|
||||
{
|
||||
int retval;
|
||||
sem_getvalue( &m_sema, &retval );
|
||||
return retval;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MutexLock Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Threading::MutexLock::MutexLock()
|
||||
{
|
||||
int err = 0;
|
||||
err = pthread_mutex_init( &mutex, NULL );
|
||||
}
|
||||
|
||||
Threading::MutexLock::~MutexLock() throw()
|
||||
{
|
||||
pthread_mutex_destroy( &mutex );
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::MutexLockRecursive() : MutexLock( false )
|
||||
{
|
||||
if( _InterlockedIncrement( &_attr_refcount ) == 1 )
|
||||
{
|
||||
if( 0 != pthread_mutexattr_init( &_attr_recursive ) )
|
||||
throw Exception::OutOfMemory( "Out of memory error initializing the Mutex attributes for recursive mutexing." );
|
||||
|
||||
pthread_mutexattr_settype( &_attr_recursive, PTHREAD_MUTEX_RECURSIVE );
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
err = pthread_mutex_init( &mutex, &_attr_recursive );
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::~MutexLockRecursive() throw()
|
||||
{
|
||||
if( _InterlockedDecrement( &_attr_refcount ) == 0 )
|
||||
pthread_mutexattr_destroy( &_attr_recursive );
|
||||
}
|
||||
|
||||
void Threading::MutexLock::Lock()
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
}
|
||||
|
||||
void Threading::MutexLock::Unlock()
|
||||
{
|
||||
pthread_mutex_unlock( &mutex );
|
||||
}
|
||||
|
||||
bool Threading::MutexLock::TryLock()
|
||||
{
|
||||
return EBUSY != pthread_mutex_trylock( &mutex );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
extern const wxTimeSpan def_yieldgui_interval;
|
||||
extern const wxTimeSpan def_deadlock_timeout;
|
||||
|
||||
extern bool _WaitGui_RecursionGuard( const char* guardname );
|
||||
}
|
||||
|
|
@ -237,6 +237,9 @@ void mtgsThreadObject::OpenPlugin()
|
|||
|
||||
void mtgsThreadObject::ExecuteTaskInThread()
|
||||
{
|
||||
// Required by the underlying SysThreadBase class (is unlocked on exit)
|
||||
m_RunningLock.Lock();
|
||||
|
||||
#ifdef RINGBUF_DEBUG_STACK
|
||||
PacketTagType prevCmd;
|
||||
#endif
|
||||
|
@ -790,9 +793,10 @@ void mtgsThreadObject::SendGameCRC( u32 crc )
|
|||
|
||||
void mtgsThreadObject::WaitForOpen()
|
||||
{
|
||||
if( !gsIsOpened )
|
||||
m_sem_OpenDone.Wait();
|
||||
m_sem_OpenDone.Reset();
|
||||
if( gsIsOpened ) return;
|
||||
Resume();
|
||||
m_sem_OpenDone.Wait();
|
||||
mtgsThread.RethrowException();
|
||||
}
|
||||
|
||||
void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
||||
|
|
|
@ -911,13 +911,12 @@ void PluginManager::Open()
|
|||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
Open( pi->id );
|
||||
// If GS doesn't support GSopen2, need to wait until call to GSopen
|
||||
// returns to populate pDsp. If it does, can initialize otherp plugins
|
||||
// returns to populate pDsp. If it does, can initialize other plugins
|
||||
// at same time as GS, as long as GSopen2 does not subclass its window.
|
||||
if (pi->id == PluginId_GS && !GSopen2) mtgsThread.WaitForOpen();
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
if (GSopen2) mtgsThread.WaitForOpen();
|
||||
mtgsThread.RethrowException();
|
||||
|
||||
Console.Status( "Plugins opened successfully." );
|
||||
}
|
||||
|
|
|
@ -35,9 +35,6 @@ static __threadlocal SysCoreThread* tls_coreThread = NULL;
|
|||
SysThreadBase::SysThreadBase() :
|
||||
m_ExecMode( ExecMode_NoThreadYet )
|
||||
, m_ExecModeMutex()
|
||||
, m_ResumeEvent()
|
||||
, m_SuspendEvent()
|
||||
, m_resume_guard( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -58,7 +55,8 @@ void SysThreadBase::OnStart()
|
|||
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
|
||||
|
||||
m_ResumeEvent.Reset();
|
||||
m_SuspendEvent.Reset();
|
||||
//m_SuspendEvent.Reset();
|
||||
FrankenMutex( m_RunningLock );
|
||||
|
||||
_parent::OnStart();
|
||||
}
|
||||
|
@ -108,11 +106,10 @@ bool SysThreadBase::Suspend( bool isBlocking )
|
|||
}
|
||||
|
||||
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
|
||||
m_SuspendEvent.Reset();
|
||||
m_sem_event.Post();
|
||||
}
|
||||
|
||||
if( isBlocking ) m_SuspendEvent.Wait();
|
||||
if( isBlocking ) m_RunningLock.Wait();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -144,11 +141,10 @@ bool SysThreadBase::Pause()
|
|||
|
||||
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
||||
|
||||
m_SuspendEvent.Reset();
|
||||
m_sem_event.Post();
|
||||
}
|
||||
|
||||
m_SuspendEvent.Wait();
|
||||
m_RunningLock.Wait();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -162,7 +158,7 @@ bool SysThreadBase::Pause()
|
|||
// before the thread has resumed. If you need explicit behavior tied to the completion of the
|
||||
// Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
|
||||
//
|
||||
// Exceptions (can occur on first call only):
|
||||
// Exceptions:
|
||||
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
|
||||
// on the first time the thread is resumed from it's initial idle state)
|
||||
// ThreadCreationError - Insufficient system resources to create thread.
|
||||
|
@ -179,8 +175,8 @@ void SysThreadBase::Resume()
|
|||
// is Suspending/Closing. Processed events could recurse into Resume, and we'll
|
||||
// want to silently ignore them.
|
||||
|
||||
RecursionGuard guard( m_resume_guard );
|
||||
if( guard.IsReentrant() ) return;
|
||||
//RecursionGuard guard( m_resume_guard );
|
||||
//if( guard.IsReentrant() ) return;
|
||||
|
||||
switch( m_ExecMode )
|
||||
{
|
||||
|
@ -196,11 +192,9 @@ void SysThreadBase::Resume()
|
|||
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||
// state before continuing...
|
||||
|
||||
locker.Unlock(); // no deadlocks please, thanks. :)
|
||||
++sys_resume_lock;
|
||||
m_SuspendEvent.Wait();
|
||||
--sys_resume_lock;
|
||||
locker.Lock();
|
||||
//locker.Unlock(); // no deadlocks please, thanks. :)
|
||||
m_RunningLock.Wait();
|
||||
//locker.Lock();
|
||||
|
||||
// The entire state coming out of a Wait is indeterminate because of user input
|
||||
// and pending messages being handled. If something doesn't feel right, we should
|
||||
|
@ -227,22 +221,22 @@ void SysThreadBase::Resume()
|
|||
|
||||
void SysThreadBase::OnCleanupInThread()
|
||||
{
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
m_ExecMode = ExecMode_NoThreadYet;
|
||||
_parent::OnCleanupInThread();
|
||||
m_RunningLock.Unlock();
|
||||
}
|
||||
|
||||
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||
{
|
||||
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||
if( m_ExecMode == ExecMode_Opened )
|
||||
/*if( m_ExecMode == ExecMode_Opened )
|
||||
{
|
||||
if( isCancelable ) TestCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Oh, seems we need a full lock, because something special is happening!
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
ScopedLock locker( m_ExecModeMutex );*/
|
||||
|
||||
switch( m_ExecMode )
|
||||
{
|
||||
|
@ -267,15 +261,16 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
|
|||
{
|
||||
OnPauseInThread();
|
||||
m_ExecMode = ExecMode_Paused;
|
||||
m_SuspendEvent.Post();
|
||||
m_RunningLock.Unlock();
|
||||
}
|
||||
// fallthrough...
|
||||
|
||||
case ExecMode_Paused:
|
||||
m_ExecModeMutex.Unlock();
|
||||
//locker.Unlock();
|
||||
while( m_ExecMode == ExecMode_Paused )
|
||||
m_ResumeEvent.WaitRaw();
|
||||
|
||||
m_RunningLock.Lock();
|
||||
OnResumeInThread( false );
|
||||
break;
|
||||
|
||||
|
@ -284,15 +279,16 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
|
|||
{
|
||||
OnSuspendInThread();
|
||||
m_ExecMode = ExecMode_Closed;
|
||||
m_SuspendEvent.Post();
|
||||
m_RunningLock.Unlock();
|
||||
}
|
||||
// fallthrough...
|
||||
|
||||
case ExecMode_Closed:
|
||||
m_ExecModeMutex.Unlock();
|
||||
//locker.Unlock();
|
||||
while( m_ExecMode == ExecMode_Closed )
|
||||
m_ResumeEvent.WaitRaw();
|
||||
|
||||
m_RunningLock.Lock();
|
||||
OnResumeInThread( true );
|
||||
break;
|
||||
|
||||
|
@ -449,6 +445,7 @@ void SysCoreThread::CpuExecute()
|
|||
|
||||
void SysCoreThread::ExecuteTaskInThread()
|
||||
{
|
||||
m_RunningLock.Lock();
|
||||
tls_coreThread = this;
|
||||
|
||||
m_sem_event.WaitRaw();
|
||||
|
@ -458,8 +455,8 @@ void SysCoreThread::ExecuteTaskInThread()
|
|||
|
||||
void SysCoreThread::OnSuspendInThread()
|
||||
{
|
||||
if( g_plugins == NULL ) return;
|
||||
g_plugins->Close();
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->Close();
|
||||
}
|
||||
|
||||
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||
|
@ -475,8 +472,9 @@ void SysCoreThread::OnResumeInThread( bool isSuspended )
|
|||
// Invoked by the pthread_exit or pthread_cancel
|
||||
void SysCoreThread::OnCleanupInThread()
|
||||
{
|
||||
//if( g_plugins != NULL )
|
||||
// g_plugins->Shutdown();
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->Close();
|
||||
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,18 @@ protected:
|
|||
};
|
||||
|
||||
volatile ExecutionMode m_ExecMode;
|
||||
MutexLock m_ExecModeMutex;
|
||||
|
||||
// This lock is used to avoid simultaneous requests to Suspend/Resume/Pause from
|
||||
// contending threads.
|
||||
MutexLockRecursive m_ExecModeMutex;
|
||||
|
||||
// Used to wake up the thread from sleeping when it's in a suspended state.
|
||||
Semaphore m_ResumeEvent;
|
||||
Semaphore m_SuspendEvent;
|
||||
int m_resume_guard;
|
||||
|
||||
// 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;
|
||||
|
||||
public:
|
||||
explicit SysThreadBase();
|
||||
|
|
|
@ -340,6 +340,10 @@ bool Pcsx2App::OnInit()
|
|||
|
||||
void Pcsx2App::CleanupMess()
|
||||
{
|
||||
// app is shutting down, so don't let the system resume for anything. (sometimes there
|
||||
// are pending Resume messages in the queue from previous user actions)
|
||||
|
||||
sys_resume_lock += 10;
|
||||
CoreThread.Cancel();
|
||||
|
||||
if( m_CorePlugins )
|
||||
|
@ -416,6 +420,6 @@ struct CrtDebugBreak
|
|||
}
|
||||
};
|
||||
|
||||
//CrtDebugBreak breakAt( 20603 );
|
||||
//CrtDebugBreak breakAt( 8890 );
|
||||
|
||||
#endif
|
|
@ -282,11 +282,10 @@ bool Pcsx2App::PrepForExit( bool canCancel )
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
|
||||
CleanupMess();
|
||||
}
|
||||
|
||||
m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
|
||||
CleanupMess();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,8 @@ protected:
|
|||
s8_Buf[u32_Read] = 0;
|
||||
OemToCharA(s8_Buf, s8_Buf); // convert DOS codepage -> ANSI
|
||||
Console.Write( m_color, s8_Buf );
|
||||
|
||||
TestCancel();
|
||||
}
|
||||
}
|
||||
catch( Exception::RuntimeError& ex )
|
||||
|
@ -207,8 +209,15 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
|||
if( 0 == SetStdHandle( stdhandle, m_writepipe ) )
|
||||
throw Exception::Win32Error( "SetStdHandle failed." );
|
||||
|
||||
// In some cases GetStdHandle can fail, even when the one we just assigned above is valid.
|
||||
// Regardless, it seems to work right so if SetStdHandle was successful, assume it worked.
|
||||
// Note: Don't use GetStdHandle to "confirm" the handle.
|
||||
//
|
||||
// Under Windows7, and possibly Vista, GetStdHandle for STDOUT will return NULL
|
||||
// after it's been assigned a custom write pipe (this differs from XP, which
|
||||
// returns the assigned handle). Amusingly, the GetStdHandle succeeds for STDERR
|
||||
// and also tends to succeed when the app is run from the MSVC debugger.
|
||||
//
|
||||
// Fortunately, there's no need to use GetStdHandle anyway, so long as SetStdHandle
|
||||
// didn't error.
|
||||
|
||||
m_crtFile = _open_osfhandle( (intptr_t)m_writepipe, _O_TEXT );
|
||||
if( m_crtFile == -1 )
|
||||
|
|
Loading…
Reference in New Issue