mirror of https://github.com/PCSX2/pcsx2.git
403 lines
12 KiB
C++
403 lines
12 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 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 <semaphore.h>
|
|
#include <errno.h> // EBUSY
|
|
#include <pthread.h>
|
|
|
|
#ifdef __APPLE__
|
|
#include <mach/semaphore.h>
|
|
#endif
|
|
|
|
#include "Pcsx2Defs.h"
|
|
#include "TraceLog.h"
|
|
|
|
#undef Yield // release the burden of windows.h global namespace spam.
|
|
|
|
#define AffinityAssert_AllowFrom_MainUI() \
|
|
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// pxThreadLog / ConsoleLogSource_Threading
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
class ConsoleLogSource_Threading : ConsoleLogSource
|
|
{
|
|
typedef ConsoleLogSource _parent;
|
|
|
|
public:
|
|
using _parent::IsActive;
|
|
|
|
ConsoleLogSource_Threading();
|
|
|
|
bool Write( const wxString& thrname, const wxChar* msg ) {
|
|
return _parent::Write( wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg );
|
|
}
|
|
bool Warn( const wxString& thrname, const wxChar* msg ) {
|
|
return _parent::Warn( wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg );
|
|
}
|
|
bool Error( const wxString& thrname, const wxChar* msg ) {
|
|
return _parent::Error( wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg );
|
|
}
|
|
};
|
|
|
|
extern ConsoleLogSource_Threading pxConLog_Thread;
|
|
|
|
#define pxThreadLog pxConLog_Thread.IsActive() && pxConLog_Thread
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// PCSX2_THREAD_LOCAL - Defines platform/operating system support for Thread Local Storage
|
|
// --------------------------------------------------------------------------------------
|
|
// For complimentary support for TLS, include Utilities/TlsVariable.inl, and use the
|
|
// DeclareTls macro in the place of __threadlocal.
|
|
//
|
|
//#define PCSX2_THREAD_LOCAL 0 // uncomment this line to force-disable native TLS (useful for testing TlsVariable on windows/linux)
|
|
|
|
#ifndef PCSX2_THREAD_LOCAL
|
|
# define PCSX2_THREAD_LOCAL 1
|
|
#endif
|
|
|
|
class wxTimeSpan;
|
|
|
|
namespace Threading
|
|
{
|
|
class pxThread;
|
|
class RwMutex;
|
|
|
|
extern void pxTestCancel();
|
|
extern pxThread* pxGetCurrentThread();
|
|
extern wxString pxGetCurrentThreadName();
|
|
extern u64 GetThreadCpuTime();
|
|
extern u64 GetThreadTicksPerSecond();
|
|
|
|
// Yields the current thread and provides cancellation points if the thread is managed by
|
|
// pxThread. Unmanaged threads use standard Sleep.
|
|
extern void pxYield( int ms );
|
|
}
|
|
|
|
namespace Exception
|
|
{
|
|
class BaseThreadError : public RuntimeError
|
|
{
|
|
DEFINE_EXCEPTION_COPYTORS( BaseThreadError, RuntimeError )
|
|
DEFINE_EXCEPTION_MESSAGES( BaseThreadError )
|
|
|
|
public:
|
|
Threading::pxThread* m_thread;
|
|
|
|
protected:
|
|
BaseThreadError() {
|
|
m_thread = NULL;
|
|
}
|
|
|
|
public:
|
|
explicit BaseThreadError( Threading::pxThread* _thread )
|
|
{
|
|
m_thread = _thread;
|
|
m_message_diag = L"An unspecified thread-related error occurred (thread=%s)";
|
|
}
|
|
|
|
explicit BaseThreadError( Threading::pxThread& _thread )
|
|
{
|
|
m_thread = &_thread;
|
|
m_message_diag = L"An unspecified thread-related error occurred (thread=%s)";
|
|
}
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
|
|
Threading::pxThread& Thread();
|
|
const Threading::pxThread& Thread() const;
|
|
};
|
|
|
|
class ThreadCreationError : public BaseThreadError
|
|
{
|
|
DEFINE_EXCEPTION_COPYTORS( ThreadCreationError, BaseThreadError )
|
|
|
|
public:
|
|
explicit ThreadCreationError( Threading::pxThread* _thread )
|
|
{
|
|
m_thread = _thread;
|
|
SetBothMsgs( L"Thread creation failure. An unspecified error occurred while trying to create the %s thread." );
|
|
}
|
|
|
|
explicit ThreadCreationError( Threading::pxThread& _thread )
|
|
{
|
|
m_thread = &_thread;
|
|
SetBothMsgs( L"Thread creation failure. An unspecified error occurred while trying to create the %s thread." );
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
namespace Threading
|
|
{
|
|
// --------------------------------------------------------------------------------------
|
|
// Platform Specific External APIs
|
|
// --------------------------------------------------------------------------------------
|
|
// The following set of documented functions have Linux/Win32 specific implementations,
|
|
// which are found in WinThreads.cpp and LnxThreads.cpp
|
|
|
|
// Releases a timeslice to other threads.
|
|
extern void Timeslice();
|
|
|
|
// For use in spin/wait loops.
|
|
extern void SpinWait();
|
|
|
|
// Optional implementation to enable hires thread/process scheduler for the operating system.
|
|
// Needed by Windows, but might not be relevant to other platforms.
|
|
extern void EnableHiresScheduler();
|
|
extern void DisableHiresScheduler();
|
|
|
|
// sleeps the current thread for the given number of milliseconds.
|
|
extern void Sleep( int ms );
|
|
|
|
// pthread Cond is an evil api that is not suited for Pcsx2 needs.
|
|
// Let's not use it. Use mutexes and semaphores instead to create waits. (Air)
|
|
#if 0
|
|
struct WaitEvent
|
|
{
|
|
pthread_cond_t cond;
|
|
pthread_mutex_t mutex;
|
|
|
|
WaitEvent();
|
|
~WaitEvent() throw();
|
|
|
|
void Set();
|
|
void Wait();
|
|
};
|
|
#endif
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// NonblockingMutex
|
|
// --------------------------------------------------------------------------------------
|
|
// This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's
|
|
// trylock(), but without any of the extra overhead needed to set up a structure capable
|
|
// of blocking waits. It basically optimizes to a single InterlockedExchange.
|
|
//
|
|
// Simple use: if TryAcquire() returns false, the Bool is already interlocked by another thread.
|
|
// If TryAcquire() returns true, you've locked the object and are *responsible* for unlocking
|
|
// it later.
|
|
//
|
|
class NonblockingMutex
|
|
{
|
|
protected:
|
|
std::atomic_flag val;
|
|
|
|
public:
|
|
NonblockingMutex() { val.clear(); }
|
|
virtual ~NonblockingMutex() throw() {}
|
|
|
|
bool TryAcquire() throw()
|
|
{
|
|
return !val.test_and_set();
|
|
}
|
|
|
|
// Can be done with a TryAcquire/Release but it is likely better to do it outside of the object
|
|
bool IsLocked()
|
|
{ pxAssertMsg(0, "IsLocked isn't supported for NonblockingMutex"); return false; }
|
|
|
|
void Release()
|
|
{
|
|
val.clear();
|
|
}
|
|
};
|
|
|
|
class Semaphore
|
|
{
|
|
protected:
|
|
#ifdef __APPLE__
|
|
semaphore_t m_sema;
|
|
int m_counter;
|
|
#else
|
|
sem_t m_sema;
|
|
#endif
|
|
|
|
public:
|
|
Semaphore();
|
|
virtual ~Semaphore() throw();
|
|
|
|
void Reset();
|
|
void Post();
|
|
void Post( int multiple );
|
|
|
|
void WaitWithoutYield();
|
|
bool WaitWithoutYield( const wxTimeSpan& timeout );
|
|
void WaitNoCancel();
|
|
void WaitNoCancel( const wxTimeSpan& timeout );
|
|
int Count();
|
|
|
|
void Wait();
|
|
bool Wait( const wxTimeSpan& timeout );
|
|
};
|
|
|
|
class Mutex
|
|
{
|
|
protected:
|
|
pthread_mutex_t m_mutex;
|
|
|
|
public:
|
|
Mutex();
|
|
virtual ~Mutex() throw();
|
|
virtual bool IsRecursive() const { return false; }
|
|
|
|
void Recreate();
|
|
bool RecreateIfLocked();
|
|
void Detach();
|
|
|
|
void Acquire();
|
|
bool Acquire( const wxTimeSpan& timeout );
|
|
bool TryAcquire();
|
|
void Release();
|
|
|
|
void AcquireWithoutYield();
|
|
bool AcquireWithoutYield( const wxTimeSpan& timeout );
|
|
|
|
void Wait();
|
|
bool Wait( const wxTimeSpan& timeout );
|
|
void WaitWithoutYield();
|
|
bool WaitWithoutYield( const wxTimeSpan& timeout );
|
|
|
|
protected:
|
|
// empty constructor used by MutexLockRecursive
|
|
Mutex( bool ) {}
|
|
};
|
|
|
|
class MutexRecursive : public Mutex
|
|
{
|
|
public:
|
|
MutexRecursive();
|
|
virtual ~MutexRecursive() throw();
|
|
virtual bool IsRecursive() const { return true; }
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ScopedLock
|
|
// --------------------------------------------------------------------------------------
|
|
// Helper class for using Mutexes. Using this class provides an exception-safe (and
|
|
// generally clean) method of locking code inside a function or conditional block. The lock
|
|
// will be automatically released on any return or exit from the function.
|
|
//
|
|
// Const qualification note:
|
|
// ScopedLock takes const instances of the mutex, even though the mutex is modified
|
|
// by locking and unlocking. Two rationales:
|
|
//
|
|
// 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes,
|
|
// this class needs a const hack to allow those accessors to be const (which is typically
|
|
// *very* important).
|
|
//
|
|
// 2) The state of the Mutex is guaranteed to be unchanged when the calling function or
|
|
// scope exits, by any means. Only via manual calls to Release or Acquire does that
|
|
// change, and typically those are only used in very special circumstances of their own.
|
|
//
|
|
class ScopedLock
|
|
{
|
|
DeclareNoncopyableObject(ScopedLock);
|
|
|
|
protected:
|
|
Mutex* m_lock;
|
|
bool m_IsLocked;
|
|
|
|
public:
|
|
virtual ~ScopedLock() throw();
|
|
explicit ScopedLock( const Mutex* locker=NULL );
|
|
explicit ScopedLock( const Mutex& locker );
|
|
void AssignAndLock( const Mutex& locker );
|
|
void AssignAndLock( const Mutex* locker );
|
|
|
|
void Assign( const Mutex& locker );
|
|
void Assign( const Mutex* locker );
|
|
|
|
void Release();
|
|
void Acquire();
|
|
|
|
bool IsLocked() const { return m_IsLocked; }
|
|
|
|
protected:
|
|
// Special constructor used by ScopedTryLock
|
|
ScopedLock( const Mutex& locker, bool isTryLock );
|
|
};
|
|
|
|
class ScopedTryLock : public ScopedLock
|
|
{
|
|
public:
|
|
ScopedTryLock( const Mutex& locker ) : ScopedLock( locker, true ) { }
|
|
virtual ~ScopedTryLock() throw() {}
|
|
bool Failed() const { return !m_IsLocked; }
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ScopedNonblockingLock
|
|
// --------------------------------------------------------------------------------------
|
|
// A ScopedTryLock branded for use with Nonblocking mutexes. See ScopedTryLock for details.
|
|
//
|
|
class ScopedNonblockingLock
|
|
{
|
|
DeclareNoncopyableObject(ScopedNonblockingLock);
|
|
|
|
protected:
|
|
NonblockingMutex& m_lock;
|
|
bool m_IsLocked;
|
|
|
|
public:
|
|
ScopedNonblockingLock( NonblockingMutex& locker ) :
|
|
m_lock( locker )
|
|
, m_IsLocked( m_lock.TryAcquire() )
|
|
{
|
|
}
|
|
|
|
virtual ~ScopedNonblockingLock() throw()
|
|
{
|
|
if( m_IsLocked )
|
|
m_lock.Release();
|
|
}
|
|
|
|
bool Failed() const { return !m_IsLocked; }
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ScopedLockBool
|
|
// --------------------------------------------------------------------------------------
|
|
// A ScopedLock in which you specify an external bool to get updated on locks/unlocks.
|
|
// Note that the isLockedBool should only be used as an indicator for the locked status,
|
|
// and not actually depended on for thread synchronization...
|
|
|
|
struct ScopedLockBool {
|
|
ScopedLock m_lock;
|
|
std::atomic<bool>& m_bool;
|
|
|
|
ScopedLockBool(Mutex& mutexToLock, std::atomic<bool>& isLockedBool)
|
|
: m_lock(mutexToLock),
|
|
m_bool(isLockedBool) {
|
|
m_bool.store(m_lock.IsLocked(), std::memory_order_relaxed);
|
|
}
|
|
virtual ~ScopedLockBool() throw() {
|
|
m_bool.store(false, std::memory_order_relaxed);
|
|
}
|
|
void Acquire() {
|
|
m_lock.Acquire();
|
|
m_bool.store(m_lock.IsLocked(), std::memory_order_relaxed);
|
|
}
|
|
void Release() {
|
|
m_bool.store(false, std::memory_order_relaxed);
|
|
m_lock.Release();
|
|
}
|
|
};
|
|
}
|
|
|