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" ?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
<CodeBlocks_project_file>
|
<CodeBlocks_project_file>
|
||||||
<FileVersion major="1" minor="6" />
|
<FileVersion major="1" minor="6" />
|
||||||
<Project>
|
<Project>
|
||||||
<Option title="Utilities" />
|
<Option title="Utilities" />
|
||||||
<Option pch_mode="2" />
|
<Option pch_mode="2" />
|
||||||
<Option compiler="gcc" />
|
<Option compiler="gcc" />
|
||||||
<Build>
|
<Build>
|
||||||
<Target title="Debug">
|
<Target title="Debug">
|
||||||
<Option output="../../../deps/debug/libUtilities" prefix_auto="1" extension_auto="1" />
|
<Option output="../../../deps/debug/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||||
<Option working_dir="" />
|
<Option working_dir="" />
|
||||||
<Option object_output="./.objs/debug/" />
|
<Option object_output="./.objs/debug/" />
|
||||||
<Option type="2" />
|
<Option type="2" />
|
||||||
<Option compiler="gcc" />
|
<Option compiler="gcc" />
|
||||||
<Option createDefFile="1" />
|
<Option createDefFile="1" />
|
||||||
<Compiler>
|
<Compiler>
|
||||||
<Add option="-Wall" />
|
<Add option="-Wall" />
|
||||||
<Add option="-g" />
|
<Add option="-g" />
|
||||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||||
<Add option="-DPCSX2_DEBUG" />
|
<Add option="-DPCSX2_DEBUG" />
|
||||||
<Add option="-DPCSX2_DEVBUILD" />
|
<Add option="-DPCSX2_DEVBUILD" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
</Target>
|
</Target>
|
||||||
<Target title="Release">
|
<Target title="Release">
|
||||||
<Option output="../../../deps/release/libUtilities" prefix_auto="1" extension_auto="1" />
|
<Option output="../../../deps/release/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||||
<Option working_dir="" />
|
<Option working_dir="" />
|
||||||
<Option object_output="./.objs/release/" />
|
<Option object_output="./.objs/release/" />
|
||||||
<Option type="2" />
|
<Option type="2" />
|
||||||
<Option compiler="gcc" />
|
<Option compiler="gcc" />
|
||||||
<Option createDefFile="1" />
|
<Option createDefFile="1" />
|
||||||
<Compiler>
|
<Compiler>
|
||||||
<Add option="-O2" />
|
<Add option="-O2" />
|
||||||
<Add option="-Wall" />
|
<Add option="-Wall" />
|
||||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=no --cflags`" />
|
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=no --cflags`" />
|
||||||
<Add option="-DNDEBUG" />
|
<Add option="-DNDEBUG" />
|
||||||
<Add directory="../../include" />
|
<Add directory="../../include" />
|
||||||
<Add directory="../../include/Utilities" />
|
<Add directory="../../include/Utilities" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
<Add option="-s" />
|
<Add option="-s" />
|
||||||
</Linker>
|
</Linker>
|
||||||
</Target>
|
</Target>
|
||||||
<Target title="Devel">
|
<Target title="Devel">
|
||||||
<Option output="../../../deps/devel/libUtilities" prefix_auto="1" extension_auto="1" />
|
<Option output="../../../deps/devel/libUtilities" prefix_auto="1" extension_auto="1" />
|
||||||
<Option working_dir="" />
|
<Option working_dir="" />
|
||||||
<Option object_output="./.objs/devel/" />
|
<Option object_output="./.objs/devel/" />
|
||||||
<Option type="2" />
|
<Option type="2" />
|
||||||
<Option compiler="gcc" />
|
<Option compiler="gcc" />
|
||||||
<Option createDefFile="1" />
|
<Option createDefFile="1" />
|
||||||
<Compiler>
|
<Compiler>
|
||||||
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
<Add option="`wx-config --version=2.8 --static=no --unicode=yes --debug=yes --cflags`" />
|
||||||
<Add option="-DPCSX2_DEVBUILD" />
|
<Add option="-DPCSX2_DEVBUILD" />
|
||||||
<Add option="-DPCSX2_DEVEL" />
|
<Add option="-DPCSX2_DEVEL" />
|
||||||
<Add option="-DNDEBUG" />
|
<Add option="-DNDEBUG" />
|
||||||
<Add directory="../../include" />
|
<Add directory="../../include" />
|
||||||
<Add directory="../../include/Utilities" />
|
<Add directory="../../include/Utilities" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
</Target>
|
</Target>
|
||||||
</Build>
|
</Build>
|
||||||
<Compiler>
|
<Compiler>
|
||||||
<Add directory="../../include/Utilities" />
|
<Add directory="../../include/Utilities" />
|
||||||
<Add directory="../../include" />
|
<Add directory="../../include" />
|
||||||
<Add directory="../../../3rdparty" />
|
<Add directory="../../../3rdparty" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Unit filename="../../../3rdparty/google/dense_hash_map" />
|
<Unit filename="../../../3rdparty/google/dense_hash_map" />
|
||||||
<Unit filename="../../../3rdparty/google/dense_hash_set" />
|
<Unit filename="../../../3rdparty/google/dense_hash_set" />
|
||||||
<Unit filename="../../../3rdparty/google/sparse_hash_map" />
|
<Unit filename="../../../3rdparty/google/sparse_hash_map" />
|
||||||
<Unit filename="../../../3rdparty/google/sparse_hash_set" />
|
<Unit filename="../../../3rdparty/google/sparse_hash_set" />
|
||||||
<Unit filename="../../../3rdparty/google/sparsehash/densehashtable.h" />
|
<Unit filename="../../../3rdparty/google/sparsehash/densehashtable.h" />
|
||||||
<Unit filename="../../../3rdparty/google/sparsehash/sparseconfig.h" />
|
<Unit filename="../../../3rdparty/google/sparsehash/sparseconfig.h" />
|
||||||
<Unit filename="../../../3rdparty/google/sparsehash/sparsehashtable.h" />
|
<Unit filename="../../../3rdparty/google/sparsehash/sparsehashtable.h" />
|
||||||
<Unit filename="../../../3rdparty/google/sparsetable" />
|
<Unit filename="../../../3rdparty/google/sparsetable" />
|
||||||
<Unit filename="../../../3rdparty/google/type_traits.h" />
|
<Unit filename="../../../3rdparty/google/type_traits.h" />
|
||||||
<Unit filename="../../include/Utilities/Console.h" />
|
<Unit filename="../../include/Utilities/Console.h" />
|
||||||
<Unit filename="../../include/Utilities/Dependencies.h" />
|
<Unit filename="../../include/Utilities/Dependencies.h" />
|
||||||
<Unit filename="../../include/Utilities/Exceptions.h" />
|
<Unit filename="../../include/Utilities/Exceptions.h" />
|
||||||
<Unit filename="../../include/Utilities/General.h" />
|
<Unit filename="../../include/Utilities/General.h" />
|
||||||
<Unit filename="../../include/Utilities/HashMap.h" />
|
<Unit filename="../../include/Utilities/HashMap.h" />
|
||||||
<Unit filename="../../include/Utilities/Listeners.h" />
|
<Unit filename="../../include/Utilities/Listeners.h" />
|
||||||
<Unit filename="../../include/Utilities/MemcpyFast.h" />
|
<Unit filename="../../include/Utilities/MemcpyFast.h" />
|
||||||
<Unit filename="../../include/Utilities/Path.h" />
|
<Unit filename="../../include/Utilities/Path.h" />
|
||||||
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
|
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
|
||||||
<Unit filename="../../include/Utilities/SafeArray.h" />
|
<Unit filename="../../include/Utilities/SafeArray.h" />
|
||||||
<Unit filename="../../include/Utilities/ScopedPtr.h" />
|
<Unit filename="../../include/Utilities/ScopedPtr.h" />
|
||||||
<Unit filename="../../include/Utilities/StringHelpers.h" />
|
<Unit filename="../../include/Utilities/StringHelpers.h" />
|
||||||
<Unit filename="../../include/Utilities/Threading.h" />
|
<Unit filename="../../include/Utilities/Threading.h" />
|
||||||
<Unit filename="../../include/Utilities/lnx_memzero.h" />
|
<Unit filename="../../include/Utilities/lnx_memzero.h" />
|
||||||
<Unit filename="../../include/Utilities/wxBaseTools.h" />
|
<Unit filename="../../include/Utilities/wxBaseTools.h" />
|
||||||
<Unit filename="../../include/Utilities/wxGuiTools.h" />
|
<Unit filename="../../include/Utilities/wxGuiTools.h" />
|
||||||
<Unit filename="../../include/intrin_x86.h" />
|
<Unit filename="../../include/intrin_x86.h" />
|
||||||
<Unit filename="../../src/Utilities/AlignedMalloc.cpp" />
|
<Unit filename="../../src/Utilities/AlignedMalloc.cpp" />
|
||||||
<Unit filename="../../src/Utilities/Console.cpp" />
|
<Unit filename="../../src/Utilities/Console.cpp" />
|
||||||
<Unit filename="../../src/Utilities/Exceptions.cpp" />
|
<Unit filename="../../src/Utilities/Exceptions.cpp" />
|
||||||
<Unit filename="../../src/Utilities/HashTools.cpp" />
|
<Unit filename="../../src/Utilities/HashTools.cpp" />
|
||||||
<Unit filename="../../src/Utilities/Linux/LnxHostSys.cpp" />
|
<Unit filename="../../src/Utilities/Linux/LnxHostSys.cpp" />
|
||||||
<Unit filename="../../src/Utilities/Linux/LnxMisc.cpp" />
|
<Unit filename="../../src/Utilities/Linux/LnxMisc.cpp" />
|
||||||
<Unit filename="../../src/Utilities/Linux/LnxThreads.cpp" />
|
<Unit filename="../../src/Utilities/Linux/LnxThreads.cpp" />
|
||||||
<Unit filename="../../src/Utilities/PathUtils.cpp" />
|
<Unit filename="../../src/Utilities/Mutex.cpp" />
|
||||||
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
|
<Unit filename="../../src/Utilities/PathUtils.cpp" />
|
||||||
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
|
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
|
||||||
<Unit filename="../../src/Utilities/ThreadTools.cpp" />
|
<Unit filename="../../src/Utilities/Semaphore.cpp" />
|
||||||
<Unit filename="../../src/Utilities/vssprintf.cpp" />
|
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
|
||||||
<Unit filename="../../src/Utilities/wxGuiTools.cpp" />
|
<Unit filename="../../src/Utilities/ThreadTools.cpp" />
|
||||||
<Unit filename="../../src/Utilities/x86/MemcpyFast.S" />
|
<Unit filename="../../src/Utilities/vssprintf.cpp" />
|
||||||
<Extensions>
|
<Unit filename="../../src/Utilities/wxGuiTools.cpp" />
|
||||||
<envvars />
|
<Unit filename="../../src/Utilities/x86/MemcpyFast.S" />
|
||||||
<code_completion>
|
<Extensions>
|
||||||
<search_path add="/usr/include/wx-2.8" />
|
<envvars />
|
||||||
</code_completion>
|
<code_completion>
|
||||||
<debugger />
|
<search_path add="/usr/include/wx-2.8" />
|
||||||
</Extensions>
|
</code_completion>
|
||||||
</Project>
|
<debugger />
|
||||||
</CodeBlocks_project_file>
|
</Extensions>
|
||||||
|
</Project>
|
||||||
|
</CodeBlocks_project_file>
|
||||||
|
|
|
@ -215,6 +215,10 @@
|
||||||
RelativePath="..\..\src\Utilities\x86\MemcpyFast.cpp"
|
RelativePath="..\..\src\Utilities\x86\MemcpyFast.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\Utilities\Mutex.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\Utilities\PathUtils.cpp"
|
RelativePath="..\..\src\Utilities\PathUtils.cpp"
|
||||||
>
|
>
|
||||||
|
@ -247,10 +251,18 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\Utilities\Semaphore.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\Utilities\StringHelpers.cpp"
|
RelativePath="..\..\src\Utilities\StringHelpers.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\Utilities\ThreadingInternal.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\Utilities\ThreadTools.cpp"
|
RelativePath="..\..\src\Utilities\ThreadTools.cpp"
|
||||||
>
|
>
|
||||||
|
|
|
@ -279,7 +279,7 @@ namespace Exception
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
// Hardware/OS Exceptions:
|
// Hardware/OS Exceptions:
|
||||||
// HardwareDeficiency / CpuStateShutdown / PluginFailure / ThreadCreationError
|
// HardwareDeficiency
|
||||||
// ---------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class HardwareDeficiency : public virtual RuntimeError
|
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.") );
|
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:
|
// Streaming (file) Exceptions:
|
||||||
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
|
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
|
||||||
|
|
|
@ -28,6 +28,33 @@ class wxTimeSpan;
|
||||||
#define AllowFromMainThreadOnly() \
|
#define AllowFromMainThreadOnly() \
|
||||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
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
|
namespace Threading
|
||||||
{
|
{
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -110,33 +137,40 @@ namespace Threading
|
||||||
void Reset();
|
void Reset();
|
||||||
void Post();
|
void Post();
|
||||||
void Post( int multiple );
|
void Post( int multiple );
|
||||||
|
|
||||||
void WaitRaw();
|
void WaitRaw();
|
||||||
bool WaitRaw( const wxTimeSpan& timeout );
|
bool WaitRaw( const wxTimeSpan& timeout );
|
||||||
void WaitNoCancel();
|
void WaitNoCancel();
|
||||||
int Count();
|
int Count();
|
||||||
|
|
||||||
#if wxUSE_GUI
|
|
||||||
void Wait();
|
void Wait();
|
||||||
bool Wait( const wxTimeSpan& timeout );
|
bool Wait( const wxTimeSpan& timeout );
|
||||||
|
|
||||||
protected:
|
|
||||||
bool _WaitGui_RecursionGuard();
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MutexLock
|
class MutexLock
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t m_mutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MutexLock();
|
MutexLock();
|
||||||
virtual ~MutexLock() throw();
|
virtual ~MutexLock() throw();
|
||||||
|
virtual bool IsRecursive() const { return false; }
|
||||||
|
|
||||||
|
void Recreate();
|
||||||
|
bool RecreateIfLocked();
|
||||||
|
void Detach();
|
||||||
|
|
||||||
void Lock();
|
void Lock();
|
||||||
void Unlock();
|
bool Lock( const wxTimeSpan& timeout );
|
||||||
bool TryLock();
|
bool TryLock();
|
||||||
|
void Unlock();
|
||||||
|
|
||||||
|
void LockRaw();
|
||||||
|
bool LockRaw( const wxTimeSpan& timeout );
|
||||||
|
|
||||||
|
void Wait();
|
||||||
|
bool Wait( const wxTimeSpan& timeout );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// empty constructor used by MutexLockRecursive
|
// empty constructor used by MutexLockRecursive
|
||||||
|
@ -148,6 +182,7 @@ namespace Threading
|
||||||
public:
|
public:
|
||||||
MutexLockRecursive();
|
MutexLockRecursive();
|
||||||
virtual ~MutexLockRecursive() throw();
|
virtual ~MutexLockRecursive() throw();
|
||||||
|
virtual bool IsRecursive() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
@ -212,7 +247,7 @@ namespace Threading
|
||||||
|
|
||||||
pthread_t m_thread;
|
pthread_t m_thread;
|
||||||
Semaphore m_sem_event; // general wait event that's needed by most threads.
|
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.
|
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
|
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
|
// exception handle, set non-NULL if the thread terminated with an exception
|
||||||
// Use RethrowException() to re-throw the exception using its original exception type.
|
// Use RethrowException() to re-throw the exception using its original exception type.
|
||||||
ScopedPtr<Exception::BaseException> m_except;
|
ScopedPtr<Exception::BaseException> m_except;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~PersistentThread() throw();
|
virtual ~PersistentThread() throw();
|
||||||
PersistentThread();
|
PersistentThread();
|
||||||
|
@ -247,10 +282,6 @@ namespace Threading
|
||||||
// Implemented by derived class to handle threading actions!
|
// Implemented by derived class to handle threading actions!
|
||||||
virtual void ExecuteTaskInThread()=0;
|
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();
|
void TestCancel();
|
||||||
|
|
||||||
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
|
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
|
||||||
|
@ -269,6 +300,8 @@ namespace Threading
|
||||||
TestCancel();
|
TestCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrankenMutex( MutexLock& mutex );
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Section of methods for internal use only.
|
// 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
|
#ifdef _WIN32
|
||||||
# include <wx/msw/wrapwin.h> // for thread renaming features
|
# include <wx/msw/wrapwin.h> // for thread renaming features
|
||||||
#endif
|
#endif
|
||||||
#include <wx/app.h>
|
|
||||||
|
|
||||||
#ifdef __LINUX__
|
#ifdef __LINUX__
|
||||||
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
||||||
|
@ -27,21 +26,35 @@
|
||||||
|
|
||||||
#include "Threading.h"
|
#include "Threading.h"
|
||||||
#include "wxBaseTools.h"
|
#include "wxBaseTools.h"
|
||||||
|
#include "ThreadingInternal.h"
|
||||||
|
|
||||||
#include <wx/datetime.h>
|
// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread,
|
||||||
#include <wx/thread.h>
|
// 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,
|
// In order to avoid deadlock we need to make sure we cut some time to handle messages.
|
||||||
// to avoid gui deadlock).
|
// But this can result in recursive yield calls, which would crash the app. Protect
|
||||||
static const wxTimeSpan ts_waitgui_interval( 0, 0, 0, 100 );
|
// 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 int __Guard = 0;
|
||||||
static const wxTimeSpan ts_waitgui_deadlock( 0, 0, 4, 0 );
|
RecursionGuard guard( __Guard );
|
||||||
|
|
||||||
static long _attr_refcount = 0;
|
if( guard.Counter >= 2 )
|
||||||
static pthread_mutexattr_t _attr_recursive;
|
{
|
||||||
|
Console.WriteLn( "(Thread Log) Possible yield recursion detected in %s; performing blocking wait.", guardname );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void Threading::Timeslice()
|
__forceinline void Threading::Timeslice()
|
||||||
|
@ -58,7 +71,7 @@ Threading::PersistentThread::PersistentThread() :
|
||||||
m_name( L"PersistentThread" )
|
m_name( L"PersistentThread" )
|
||||||
, m_thread()
|
, m_thread()
|
||||||
, m_sem_event()
|
, m_sem_event()
|
||||||
, m_sem_finished()
|
, m_lock_InThread()
|
||||||
, m_lock_start()
|
, m_lock_start()
|
||||||
, m_detached( true ) // start out with m_thread in detached/invalid state
|
, m_detached( true ) // start out with m_thread in detached/invalid state
|
||||||
, m_running( false )
|
, m_running( false )
|
||||||
|
@ -81,14 +94,7 @@ Threading::PersistentThread::~PersistentThread() throw()
|
||||||
if( m_running )
|
if( m_running )
|
||||||
{
|
{
|
||||||
DevCon.WriteLn( L"\tWaiting for running thread to end...");
|
DevCon.WriteLn( L"\tWaiting for running thread to end...");
|
||||||
#if wxUSE_GUI
|
m_lock_InThread.Wait();
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
Threading::Sleep( 1 );
|
Threading::Sleep( 1 );
|
||||||
Detach();
|
Detach();
|
||||||
|
@ -110,6 +116,19 @@ Threading::PersistentThread::~PersistentThread() throw()
|
||||||
DESTRUCTOR_CATCHALL
|
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
|
// 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
|
// 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
|
// 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.
|
// This function should not be called from the owner thread.
|
||||||
void Threading::PersistentThread::Start()
|
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;
|
if( m_running ) return;
|
||||||
|
|
||||||
Detach(); // clean up previous thread handle, if one exists.
|
Detach(); // clean up previous thread handle, if one exists.
|
||||||
m_sem_finished.Reset();
|
|
||||||
|
FrankenMutex( m_lock_InThread );
|
||||||
|
|
||||||
OnStart();
|
OnStart();
|
||||||
|
|
||||||
|
@ -159,23 +180,25 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
|
||||||
{
|
{
|
||||||
pxAssertMsg( !IsSelf(), "Thread affinity error." );
|
pxAssertMsg( !IsSelf(), "Thread affinity error." );
|
||||||
|
|
||||||
if( !m_running ) return;
|
|
||||||
|
|
||||||
if( m_detached )
|
|
||||||
{
|
{
|
||||||
Console.Notice( "(Thread Warning) Ignoring attempted cancelation of detached thread." );
|
// Prevent simultaneous startup and cancel:
|
||||||
return;
|
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( isBlocking )
|
||||||
{
|
{
|
||||||
#if wxUSE_GUI
|
m_lock_InThread.Wait();
|
||||||
m_sem_finished.Wait();
|
Detach();
|
||||||
#else
|
|
||||||
m_sem_finished.WaitRaw();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,12 +214,7 @@ void Threading::PersistentThread::Block()
|
||||||
{
|
{
|
||||||
pxAssertDev( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
pxAssertDev( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
||||||
|
|
||||||
if( m_running )
|
m_lock_InThread.Wait();
|
||||||
#if wxUSE_GUI
|
|
||||||
m_sem_finished.Wait();
|
|
||||||
#else
|
|
||||||
m_sem_finished.WaitRaw();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Threading::PersistentThread::IsSelf() const
|
bool Threading::PersistentThread::IsSelf() const
|
||||||
|
@ -218,6 +236,10 @@ void Threading::PersistentThread::RethrowException() const
|
||||||
m_except->Rethrow();
|
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()
|
void Threading::PersistentThread::TestCancel()
|
||||||
{
|
{
|
||||||
pxAssert( IsSelf() );
|
pxAssert( IsSelf() );
|
||||||
|
@ -297,15 +319,10 @@ void Threading::PersistentThread::_ThreadCleanup()
|
||||||
{
|
{
|
||||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
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 );
|
_try_virtual_invoke( &PersistentThread::OnCleanupInThread );
|
||||||
|
|
||||||
m_running = false;
|
m_running = false;
|
||||||
m_sem_finished.Post();
|
m_lock_InThread.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString Threading::PersistentThread::GetName() const
|
wxString Threading::PersistentThread::GetName() const
|
||||||
|
@ -315,6 +332,7 @@ wxString Threading::PersistentThread::GetName() const
|
||||||
|
|
||||||
void Threading::PersistentThread::_internal_execute()
|
void Threading::PersistentThread::_internal_execute()
|
||||||
{
|
{
|
||||||
|
m_lock_InThread.Lock();
|
||||||
m_running = true;
|
m_running = true;
|
||||||
_DoSetThreadName( m_name );
|
_DoSetThreadName( m_name );
|
||||||
_try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
|
_try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
|
||||||
|
@ -472,228 +490,6 @@ void Threading::WaitEvent::Wait()
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
// 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()
|
void mtgsThreadObject::ExecuteTaskInThread()
|
||||||
{
|
{
|
||||||
|
// Required by the underlying SysThreadBase class (is unlocked on exit)
|
||||||
|
m_RunningLock.Lock();
|
||||||
|
|
||||||
#ifdef RINGBUF_DEBUG_STACK
|
#ifdef RINGBUF_DEBUG_STACK
|
||||||
PacketTagType prevCmd;
|
PacketTagType prevCmd;
|
||||||
#endif
|
#endif
|
||||||
|
@ -790,9 +793,10 @@ void mtgsThreadObject::SendGameCRC( u32 crc )
|
||||||
|
|
||||||
void mtgsThreadObject::WaitForOpen()
|
void mtgsThreadObject::WaitForOpen()
|
||||||
{
|
{
|
||||||
if( !gsIsOpened )
|
if( gsIsOpened ) return;
|
||||||
m_sem_OpenDone.Wait();
|
Resume();
|
||||||
m_sem_OpenDone.Reset();
|
m_sem_OpenDone.Wait();
|
||||||
|
mtgsThread.RethrowException();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
||||||
|
|
|
@ -911,13 +911,12 @@ void PluginManager::Open()
|
||||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||||
Open( pi->id );
|
Open( pi->id );
|
||||||
// If GS doesn't support GSopen2, need to wait until call to GSopen
|
// 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.
|
// at same time as GS, as long as GSopen2 does not subclass its window.
|
||||||
if (pi->id == PluginId_GS && !GSopen2) mtgsThread.WaitForOpen();
|
if (pi->id == PluginId_GS && !GSopen2) mtgsThread.WaitForOpen();
|
||||||
} while( ++pi, pi->shortname != NULL );
|
} while( ++pi, pi->shortname != NULL );
|
||||||
|
|
||||||
if (GSopen2) mtgsThread.WaitForOpen();
|
if (GSopen2) mtgsThread.WaitForOpen();
|
||||||
mtgsThread.RethrowException();
|
|
||||||
|
|
||||||
Console.Status( "Plugins opened successfully." );
|
Console.Status( "Plugins opened successfully." );
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,6 @@ static __threadlocal SysCoreThread* tls_coreThread = NULL;
|
||||||
SysThreadBase::SysThreadBase() :
|
SysThreadBase::SysThreadBase() :
|
||||||
m_ExecMode( ExecMode_NoThreadYet )
|
m_ExecMode( ExecMode_NoThreadYet )
|
||||||
, m_ExecModeMutex()
|
, 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;
|
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
|
||||||
|
|
||||||
m_ResumeEvent.Reset();
|
m_ResumeEvent.Reset();
|
||||||
m_SuspendEvent.Reset();
|
//m_SuspendEvent.Reset();
|
||||||
|
FrankenMutex( m_RunningLock );
|
||||||
|
|
||||||
_parent::OnStart();
|
_parent::OnStart();
|
||||||
}
|
}
|
||||||
|
@ -108,11 +106,10 @@ bool SysThreadBase::Suspend( bool isBlocking )
|
||||||
}
|
}
|
||||||
|
|
||||||
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
|
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
|
||||||
m_SuspendEvent.Reset();
|
|
||||||
m_sem_event.Post();
|
m_sem_event.Post();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isBlocking ) m_SuspendEvent.Wait();
|
if( isBlocking ) m_RunningLock.Wait();
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,11 +141,10 @@ bool SysThreadBase::Pause()
|
||||||
|
|
||||||
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
||||||
|
|
||||||
m_SuspendEvent.Reset();
|
|
||||||
m_sem_event.Post();
|
m_sem_event.Post();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_SuspendEvent.Wait();
|
m_RunningLock.Wait();
|
||||||
|
|
||||||
return retval;
|
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
|
// 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.
|
// 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
|
// 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)
|
// on the first time the thread is resumed from it's initial idle state)
|
||||||
// ThreadCreationError - Insufficient system resources to create thread.
|
// 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
|
// is Suspending/Closing. Processed events could recurse into Resume, and we'll
|
||||||
// want to silently ignore them.
|
// want to silently ignore them.
|
||||||
|
|
||||||
RecursionGuard guard( m_resume_guard );
|
//RecursionGuard guard( m_resume_guard );
|
||||||
if( guard.IsReentrant() ) return;
|
//if( guard.IsReentrant() ) return;
|
||||||
|
|
||||||
switch( m_ExecMode )
|
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
|
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||||
// state before continuing...
|
// state before continuing...
|
||||||
|
|
||||||
locker.Unlock(); // no deadlocks please, thanks. :)
|
//locker.Unlock(); // no deadlocks please, thanks. :)
|
||||||
++sys_resume_lock;
|
m_RunningLock.Wait();
|
||||||
m_SuspendEvent.Wait();
|
//locker.Lock();
|
||||||
--sys_resume_lock;
|
|
||||||
locker.Lock();
|
|
||||||
|
|
||||||
// The entire state coming out of a Wait is indeterminate because of user input
|
// 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
|
// and pending messages being handled. If something doesn't feel right, we should
|
||||||
|
@ -227,22 +221,22 @@ void SysThreadBase::Resume()
|
||||||
|
|
||||||
void SysThreadBase::OnCleanupInThread()
|
void SysThreadBase::OnCleanupInThread()
|
||||||
{
|
{
|
||||||
ScopedLock locker( m_ExecModeMutex );
|
|
||||||
m_ExecMode = ExecMode_NoThreadYet;
|
m_ExecMode = ExecMode_NoThreadYet;
|
||||||
_parent::OnCleanupInThread();
|
_parent::OnCleanupInThread();
|
||||||
|
m_RunningLock.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||||
{
|
{
|
||||||
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||||
if( m_ExecMode == ExecMode_Opened )
|
/*if( m_ExecMode == ExecMode_Opened )
|
||||||
{
|
{
|
||||||
if( isCancelable ) TestCancel();
|
if( isCancelable ) TestCancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oh, seems we need a full lock, because something special is happening!
|
// Oh, seems we need a full lock, because something special is happening!
|
||||||
ScopedLock locker( m_ExecModeMutex );
|
ScopedLock locker( m_ExecModeMutex );*/
|
||||||
|
|
||||||
switch( m_ExecMode )
|
switch( m_ExecMode )
|
||||||
{
|
{
|
||||||
|
@ -267,15 +261,16 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||||
{
|
{
|
||||||
OnPauseInThread();
|
OnPauseInThread();
|
||||||
m_ExecMode = ExecMode_Paused;
|
m_ExecMode = ExecMode_Paused;
|
||||||
m_SuspendEvent.Post();
|
m_RunningLock.Unlock();
|
||||||
}
|
}
|
||||||
// fallthrough...
|
// fallthrough...
|
||||||
|
|
||||||
case ExecMode_Paused:
|
case ExecMode_Paused:
|
||||||
m_ExecModeMutex.Unlock();
|
//locker.Unlock();
|
||||||
while( m_ExecMode == ExecMode_Paused )
|
while( m_ExecMode == ExecMode_Paused )
|
||||||
m_ResumeEvent.WaitRaw();
|
m_ResumeEvent.WaitRaw();
|
||||||
|
|
||||||
|
m_RunningLock.Lock();
|
||||||
OnResumeInThread( false );
|
OnResumeInThread( false );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -284,15 +279,16 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||||
{
|
{
|
||||||
OnSuspendInThread();
|
OnSuspendInThread();
|
||||||
m_ExecMode = ExecMode_Closed;
|
m_ExecMode = ExecMode_Closed;
|
||||||
m_SuspendEvent.Post();
|
m_RunningLock.Unlock();
|
||||||
}
|
}
|
||||||
// fallthrough...
|
// fallthrough...
|
||||||
|
|
||||||
case ExecMode_Closed:
|
case ExecMode_Closed:
|
||||||
m_ExecModeMutex.Unlock();
|
//locker.Unlock();
|
||||||
while( m_ExecMode == ExecMode_Closed )
|
while( m_ExecMode == ExecMode_Closed )
|
||||||
m_ResumeEvent.WaitRaw();
|
m_ResumeEvent.WaitRaw();
|
||||||
|
|
||||||
|
m_RunningLock.Lock();
|
||||||
OnResumeInThread( true );
|
OnResumeInThread( true );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -449,6 +445,7 @@ void SysCoreThread::CpuExecute()
|
||||||
|
|
||||||
void SysCoreThread::ExecuteTaskInThread()
|
void SysCoreThread::ExecuteTaskInThread()
|
||||||
{
|
{
|
||||||
|
m_RunningLock.Lock();
|
||||||
tls_coreThread = this;
|
tls_coreThread = this;
|
||||||
|
|
||||||
m_sem_event.WaitRaw();
|
m_sem_event.WaitRaw();
|
||||||
|
@ -458,8 +455,8 @@ void SysCoreThread::ExecuteTaskInThread()
|
||||||
|
|
||||||
void SysCoreThread::OnSuspendInThread()
|
void SysCoreThread::OnSuspendInThread()
|
||||||
{
|
{
|
||||||
if( g_plugins == NULL ) return;
|
if( g_plugins != NULL )
|
||||||
g_plugins->Close();
|
g_plugins->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||||
|
@ -475,8 +472,9 @@ void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||||
// Invoked by the pthread_exit or pthread_cancel
|
// Invoked by the pthread_exit or pthread_cancel
|
||||||
void SysCoreThread::OnCleanupInThread()
|
void SysCoreThread::OnCleanupInThread()
|
||||||
{
|
{
|
||||||
//if( g_plugins != NULL )
|
if( g_plugins != NULL )
|
||||||
// g_plugins->Shutdown();
|
g_plugins->Close();
|
||||||
|
|
||||||
_parent::OnCleanupInThread();
|
_parent::OnCleanupInThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,18 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
volatile ExecutionMode m_ExecMode;
|
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_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:
|
public:
|
||||||
explicit SysThreadBase();
|
explicit SysThreadBase();
|
||||||
|
|
|
@ -340,6 +340,10 @@ bool Pcsx2App::OnInit()
|
||||||
|
|
||||||
void Pcsx2App::CleanupMess()
|
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();
|
CoreThread.Cancel();
|
||||||
|
|
||||||
if( m_CorePlugins )
|
if( m_CorePlugins )
|
||||||
|
@ -416,6 +420,6 @@ struct CrtDebugBreak
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//CrtDebugBreak breakAt( 20603 );
|
//CrtDebugBreak breakAt( 8890 );
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -282,11 +282,10 @@ bool Pcsx2App::PrepForExit( bool canCancel )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
|
||||||
m_evtsrc_AppStatus.Dispatch( AppStatus_Exiting );
|
CleanupMess();
|
||||||
CleanupMess();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,8 @@ protected:
|
||||||
s8_Buf[u32_Read] = 0;
|
s8_Buf[u32_Read] = 0;
|
||||||
OemToCharA(s8_Buf, s8_Buf); // convert DOS codepage -> ANSI
|
OemToCharA(s8_Buf, s8_Buf); // convert DOS codepage -> ANSI
|
||||||
Console.Write( m_color, s8_Buf );
|
Console.Write( m_color, s8_Buf );
|
||||||
|
|
||||||
|
TestCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( Exception::RuntimeError& ex )
|
catch( Exception::RuntimeError& ex )
|
||||||
|
@ -207,8 +209,15 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
||||||
if( 0 == SetStdHandle( stdhandle, m_writepipe ) )
|
if( 0 == SetStdHandle( stdhandle, m_writepipe ) )
|
||||||
throw Exception::Win32Error( "SetStdHandle failed." );
|
throw Exception::Win32Error( "SetStdHandle failed." );
|
||||||
|
|
||||||
// In some cases GetStdHandle can fail, even when the one we just assigned above is valid.
|
// Note: Don't use GetStdHandle to "confirm" the handle.
|
||||||
// Regardless, it seems to work right so if SetStdHandle was successful, assume it worked.
|
//
|
||||||
|
// 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 );
|
m_crtFile = _open_osfhandle( (intptr_t)m_writepipe, _O_TEXT );
|
||||||
if( m_crtFile == -1 )
|
if( m_crtFile == -1 )
|
||||||
|
|
Loading…
Reference in New Issue