2009-10-17 18:21:30 +00:00
|
|
|
/* 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"
|
2009-11-01 09:27:16 +00:00
|
|
|
#include "wxGuiTools.h"
|
2009-10-17 18:21:30 +00:00
|
|
|
#include "ThreadingInternal.h"
|
|
|
|
|
|
|
|
namespace Threading
|
|
|
|
{
|
|
|
|
static long _attr_refcount = 0;
|
|
|
|
static pthread_mutexattr_t _attr_recursive;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
2009-10-31 19:18:29 +00:00
|
|
|
// Mutex Implementations
|
2009-10-17 18:21:30 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
Threading::Mutex::Mutex()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-10-17 18:51:17 +00:00
|
|
|
pthread_mutex_init( &m_mutex, NULL );
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
static wxTimeSpan def_detach_timeout( 0, 0, 6, 0 );
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
void Threading::Mutex::Detach()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
if( EBUSY != pthread_mutex_destroy(&m_mutex) ) return;
|
2009-10-17 18:51:17 +00:00
|
|
|
|
2009-10-17 18:21:30 +00:00
|
|
|
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)
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
Release(); Release(); // in case of double recursion.
|
2009-10-17 18:21:30 +00:00
|
|
|
int result = pthread_mutex_destroy( &m_mutex );
|
|
|
|
if( pxAssertDev( result != EBUSY, "Detachment of a recursively-locked mutex (self-locked!)." ) ) return;
|
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
if( Wait(def_detach_timeout) )
|
2009-10-17 18:21:30 +00:00
|
|
|
pthread_mutex_destroy( &m_mutex );
|
|
|
|
else
|
|
|
|
Console.Error( "(Thread Log) Mutex cleanup failed due to possible deadlock.");
|
|
|
|
}
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
Threading::Mutex::~Mutex() throw()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
try {
|
2009-10-31 19:18:29 +00:00
|
|
|
Mutex::Detach();
|
2009-10-17 18:21:30 +00:00
|
|
|
} DESTRUCTOR_CATCHALL;
|
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
Threading::MutexRecursive::MutexRecursive() : Mutex( false )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
Threading::MutexRecursive::~MutexRecursive() throw()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
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.
|
2009-10-31 19:18:29 +00:00
|
|
|
void Threading::Mutex::Recreate()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
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.
|
2009-10-31 19:18:29 +00:00
|
|
|
bool Threading::Mutex::RecreateIfLocked()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2010-04-27 13:12:03 +00:00
|
|
|
if( !Wait(def_detach_timeout) )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
Recreate();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
// This is a direct blocking action -- very fast, very efficient, and generally very dangerous
|
|
|
|
// if used from the main GUI thread, since it typically results in an unresponsive program.
|
|
|
|
// Call this method directly only if you know the code in question will be run from threads
|
2010-04-25 00:31:27 +00:00
|
|
|
// other than the main thread.
|
2009-11-01 09:27:16 +00:00
|
|
|
void Threading::Mutex::AcquireWithoutYield()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
pxAssertMsg( !wxThread::IsMain(), "Unyielding mutex acquire issued from the main/gui thread. Please use Acquire() instead." );
|
2009-10-17 18:21:30 +00:00
|
|
|
pthread_mutex_lock( &m_mutex );
|
|
|
|
}
|
|
|
|
|
2009-11-01 09:27:16 +00:00
|
|
|
bool Threading::Mutex::AcquireWithoutYield( const wxTimeSpan& timeout )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
wxDateTime megafail( wxDateTime::UNow() + timeout );
|
|
|
|
const timespec fail = { megafail.GetTicks(), megafail.GetMillisecond() * 1000000 };
|
|
|
|
return pthread_mutex_timedlock( &m_mutex, &fail ) == 0;
|
|
|
|
}
|
|
|
|
|
2009-10-31 19:18:29 +00:00
|
|
|
void Threading::Mutex::Release()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
pthread_mutex_unlock( &m_mutex );
|
|
|
|
}
|
|
|
|
|
2009-10-31 19:25:46 +00:00
|
|
|
bool Threading::Mutex::TryAcquire()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
return EBUSY != pthread_mutex_trylock( &m_mutex );
|
|
|
|
}
|
|
|
|
|
2009-11-01 09:27:16 +00:00
|
|
|
// This is a wxApp-safe rendition of AcquireWithoutYield, which makes sure to execute pending app events
|
2009-10-17 18:21:30 +00:00
|
|
|
// and messages *if* the lock is performed from the main GUI thread.
|
|
|
|
//
|
|
|
|
// Exceptions:
|
2010-01-22 15:22:01 +00:00
|
|
|
// ThreadDeadlock - See description of ThreadDeadlock for details
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
2009-10-31 19:25:46 +00:00
|
|
|
void Threading::Mutex::Acquire()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
#if wxUSE_GUI
|
|
|
|
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
pthread_mutex_lock( &m_mutex );
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
2009-10-31 19:25:46 +00:00
|
|
|
else if( _WaitGui_RecursionGuard( "Mutex::Acquire" ) )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2010-04-27 13:12:03 +00:00
|
|
|
AcquireWithoutYield();
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
while( !AcquireWithoutYield(def_yieldgui_interval) )
|
2009-10-17 18:21:30 +00:00
|
|
|
wxTheApp->Yield( true );
|
|
|
|
}
|
|
|
|
#else
|
2009-11-01 09:27:16 +00:00
|
|
|
pthread_mutex_lock( &m_mutex );
|
2009-10-17 18:21:30 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exceptions:
|
2010-01-22 15:22:01 +00:00
|
|
|
// ThreadDeadlock - See description of ThreadDeadlock for details
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
2009-10-31 19:25:46 +00:00
|
|
|
bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
|
|
|
#if wxUSE_GUI
|
|
|
|
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
return AcquireWithoutYield(timeout);
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
2009-10-31 19:25:46 +00:00
|
|
|
else if( _WaitGui_RecursionGuard( "Mutex::Acquire(timeout)" ) )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
|
|
|
|
return AcquireWithoutYield( timeout );
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-01 09:27:16 +00:00
|
|
|
ScopedBusyCursor hourglass( Cursor_KindaBusy );
|
2009-10-17 18:21:30 +00:00
|
|
|
wxTimeSpan countdown( (timeout) );
|
|
|
|
|
|
|
|
do {
|
2009-11-01 09:27:16 +00:00
|
|
|
if( AcquireWithoutYield( def_yieldgui_interval ) ) break;
|
2009-10-18 12:30:00 +00:00
|
|
|
wxTheApp->Yield(true);
|
2009-10-17 18:21:30 +00:00
|
|
|
countdown -= def_yieldgui_interval;
|
|
|
|
} while( countdown.GetMilliseconds() > 0 );
|
|
|
|
|
|
|
|
return countdown.GetMilliseconds() > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2009-11-01 09:27:16 +00:00
|
|
|
return AcquireWithoutYield();
|
2009-10-17 18:21:30 +00:00
|
|
|
#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).
|
|
|
|
//
|
2009-10-31 19:25:46 +00:00
|
|
|
// Implemented internally as a simple Acquire/Release pair.
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
|
|
|
// Exceptions:
|
2010-01-22 15:22:01 +00:00
|
|
|
// ThreadDeadlock - See description of ThreadDeadlock for details
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
2009-10-31 19:18:29 +00:00
|
|
|
void Threading::Mutex::Wait()
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-10-31 19:25:46 +00:00
|
|
|
Acquire();
|
2009-10-31 19:18:29 +00:00
|
|
|
Release();
|
2009-10-17 18:21:30 +00:00
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
void Threading::Mutex::WaitWithoutYield()
|
|
|
|
{
|
|
|
|
AcquireWithoutYield();
|
|
|
|
Release();
|
|
|
|
}
|
|
|
|
|
2009-10-17 18:21:30 +00:00
|
|
|
// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked.
|
2009-10-31 19:25:46 +00:00
|
|
|
// (Implemented internally as a simple Acquire/Release pair.)
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
|
|
|
// 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:
|
2010-01-22 15:22:01 +00:00
|
|
|
// ThreadDeadlock - See description of ThreadDeadlock for details
|
2009-10-17 18:21:30 +00:00
|
|
|
//
|
2009-10-31 19:18:29 +00:00
|
|
|
bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-10-31 19:25:46 +00:00
|
|
|
if( Acquire(timeout) )
|
2009-10-17 18:21:30 +00:00
|
|
|
{
|
2009-10-31 19:18:29 +00:00
|
|
|
Release();
|
2009-10-17 18:21:30 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-04-27 13:12:03 +00:00
|
|
|
bool Threading::Mutex::WaitWithoutYield( const wxTimeSpan& timeout )
|
|
|
|
{
|
|
|
|
if( AcquireWithoutYield(timeout) )
|
|
|
|
{
|
|
|
|
Release();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// ScopedLock Implementations
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
Threading::ScopedLock::~ScopedLock() throw()
|
|
|
|
{
|
|
|
|
if( m_IsLocked && m_lock )
|
|
|
|
m_lock->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
Threading::ScopedLock::ScopedLock( const Mutex* locker )
|
|
|
|
{
|
|
|
|
AssignAndLock( locker );
|
|
|
|
}
|
|
|
|
|
|
|
|
Threading::ScopedLock::ScopedLock( const Mutex& locker )
|
|
|
|
{
|
|
|
|
AssignAndLock( locker );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Threading::ScopedLock::AssignAndLock( const Mutex& locker )
|
|
|
|
{
|
|
|
|
AssignAndLock( &locker );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Threading::ScopedLock::AssignAndLock( const Mutex* locker )
|
|
|
|
{
|
|
|
|
m_lock = const_cast<Mutex*>(locker);
|
|
|
|
if( !m_lock ) return;
|
|
|
|
|
|
|
|
m_IsLocked = true;
|
|
|
|
m_lock->Acquire();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provides manual unlocking of a scoped lock prior to object destruction.
|
|
|
|
void Threading::ScopedLock::Release()
|
|
|
|
{
|
|
|
|
if( !m_IsLocked ) return;
|
|
|
|
m_IsLocked = false;
|
|
|
|
if( m_lock ) m_lock->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
|
|
|
|
void Threading::ScopedLock::Acquire()
|
|
|
|
{
|
|
|
|
if( m_IsLocked || !m_lock ) return;
|
|
|
|
m_lock->Acquire();
|
|
|
|
m_IsLocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Threading::ScopedLock::ScopedLock( const Mutex& locker, bool isTryLock )
|
|
|
|
{
|
|
|
|
m_lock = const_cast<Mutex*>(&locker);
|
|
|
|
if( !m_lock ) return;
|
|
|
|
m_IsLocked = isTryLock ? m_lock->TryAcquire() : false;
|
|
|
|
}
|