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:
Jake.Stine 2009-10-17 18:21:30 +00:00
parent c3cbdaf016
commit d69f6610e8
15 changed files with 740 additions and 451 deletions

View File

@ -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>

View 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"
>

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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 );
}

View File

@ -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 )

View File

@ -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." );
}

View File

@ -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();
}

View File

@ -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();

View File

@ -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

View File

@ -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;
}

View File

@ -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 )