mirror of https://github.com/PCSX2/pcsx2.git
485 lines
9.9 KiB
C++
485 lines
9.9 KiB
C++
/*
|
|
* Copyright (C) 2007-2009 Gabest
|
|
* http://www.gabest.org
|
|
*
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This Program 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 GNU Make; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "GSdx.h"
|
|
|
|
class IGSThread
|
|
{
|
|
protected:
|
|
virtual void ThreadProc() = 0;
|
|
};
|
|
|
|
class IGSLock
|
|
{
|
|
public:
|
|
virtual void Lock() = 0;
|
|
virtual bool TryLock() = 0;
|
|
virtual void Unlock() = 0;
|
|
virtual ~IGSLock() {}
|
|
};
|
|
|
|
class IGSEvent
|
|
{
|
|
public:
|
|
virtual void Set() = 0;
|
|
virtual bool Wait(IGSLock* l) = 0;
|
|
virtual ~IGSEvent() {}
|
|
};
|
|
|
|
#ifdef _WINDOWS
|
|
|
|
typedef void (WINAPI * InitializeConditionVariablePtr)(CONDITION_VARIABLE* ConditionVariable);
|
|
typedef void (WINAPI * WakeConditionVariablePtr)(CONDITION_VARIABLE* ConditionVariable);
|
|
typedef void (WINAPI * WakeAllConditionVariablePtr)(CONDITION_VARIABLE* ConditionVariable);
|
|
typedef BOOL (WINAPI * SleepConditionVariableSRWPtr)(CONDITION_VARIABLE* ConditionVariable, SRWLOCK* SRWLock, DWORD dwMilliseconds, ULONG Flags);
|
|
typedef void (WINAPI * InitializeSRWLockPtr)(SRWLOCK* SRWLock);
|
|
typedef void (WINAPI * AcquireSRWLockExclusivePtr)(SRWLOCK* SRWLock);
|
|
typedef BOOLEAN (WINAPI * TryAcquireSRWLockExclusivePtr)(SRWLOCK* SRWLock);
|
|
typedef void (WINAPI * ReleaseSRWLockExclusivePtr)(SRWLOCK* SRWLock);
|
|
typedef void (WINAPI * AcquireSRWLockSharedPtr)(SRWLOCK* SRWLock);
|
|
typedef BOOLEAN (WINAPI * TryAcquireSRWLockSharedPtr)(SRWLOCK* SRWLock);
|
|
typedef void (WINAPI * ReleaseSRWLockSharedPtr)(SRWLOCK* SRWLock);
|
|
|
|
extern InitializeConditionVariablePtr pInitializeConditionVariable;
|
|
extern WakeConditionVariablePtr pWakeConditionVariable;
|
|
extern WakeAllConditionVariablePtr pWakeAllConditionVariable;
|
|
extern SleepConditionVariableSRWPtr pSleepConditionVariableSRW;
|
|
extern InitializeSRWLockPtr pInitializeSRWLock;
|
|
extern AcquireSRWLockExclusivePtr pAcquireSRWLockExclusive;
|
|
extern TryAcquireSRWLockExclusivePtr pTryAcquireSRWLockExclusive;
|
|
extern ReleaseSRWLockExclusivePtr pReleaseSRWLockExclusive;
|
|
extern AcquireSRWLockSharedPtr pAcquireSRWLockShared;
|
|
extern TryAcquireSRWLockSharedPtr pTryAcquireSRWLockShared;
|
|
extern ReleaseSRWLockSharedPtr pReleaseSRWLockShared;
|
|
|
|
class GSThread : public IGSThread
|
|
{
|
|
DWORD m_ThreadId;
|
|
HANDLE m_hThread;
|
|
|
|
static DWORD WINAPI StaticThreadProc(void* lpParam);
|
|
|
|
protected:
|
|
void CreateThread();
|
|
void CloseThread();
|
|
|
|
public:
|
|
GSThread();
|
|
virtual ~GSThread();
|
|
};
|
|
|
|
class GSCritSec : public IGSLock
|
|
{
|
|
CRITICAL_SECTION m_cs;
|
|
|
|
public:
|
|
GSCritSec() {InitializeCriticalSection(&m_cs);}
|
|
~GSCritSec() {DeleteCriticalSection(&m_cs);}
|
|
|
|
void Lock() {EnterCriticalSection(&m_cs);}
|
|
bool TryLock() {return TryEnterCriticalSection(&m_cs) == TRUE;}
|
|
void Unlock() {LeaveCriticalSection(&m_cs);}
|
|
};
|
|
|
|
class GSEvent : public IGSEvent
|
|
{
|
|
protected:
|
|
HANDLE m_hEvent;
|
|
|
|
public:
|
|
GSEvent() {m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);}
|
|
~GSEvent() {CloseHandle(m_hEvent);}
|
|
|
|
void Set() {SetEvent(m_hEvent);}
|
|
bool Wait(IGSLock* l) {if(l) l->Unlock(); bool b = WaitForSingleObject(m_hEvent, INFINITE) == WAIT_OBJECT_0; if(l) l->Lock(); return b;}
|
|
};
|
|
|
|
class GSCondVarLock : public IGSLock
|
|
{
|
|
SRWLOCK m_lock;
|
|
|
|
public:
|
|
GSCondVarLock() {pInitializeSRWLock(&m_lock);}
|
|
|
|
void Lock() {pAcquireSRWLockExclusive(&m_lock);}
|
|
bool TryLock() {return pTryAcquireSRWLockExclusive(&m_lock) == TRUE;}
|
|
void Unlock() {pReleaseSRWLockExclusive(&m_lock);}
|
|
|
|
operator SRWLOCK* () {return &m_lock;}
|
|
};
|
|
|
|
class GSCondVar : public IGSEvent
|
|
{
|
|
CONDITION_VARIABLE m_cv;
|
|
|
|
public:
|
|
GSCondVar() {pInitializeConditionVariable(&m_cv);}
|
|
|
|
void Set() {pWakeConditionVariable(&m_cv);}
|
|
bool Wait(IGSLock* l) {return pSleepConditionVariableSRW(&m_cv, *(GSCondVarLock*)l, INFINITE, 0) != 0;}
|
|
|
|
operator CONDITION_VARIABLE* () {return &m_cv;}
|
|
};
|
|
|
|
#else
|
|
// let us use std::thread for now, comment out the definition to go back to pthread
|
|
// There are currently some bugs/limitations to std::thread (see various comment)
|
|
// For the moment let's keep pthread but uses new std object (mutex, cond_var)
|
|
//#define _STD_THREAD_
|
|
#ifdef _STD_THREAD_
|
|
#include <thread>
|
|
#else
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
class GSThread : public IGSThread
|
|
{
|
|
#ifdef _STD_THREAD_
|
|
std::thread *t;
|
|
#else
|
|
pthread_attr_t m_thread_attr;
|
|
pthread_t m_thread;
|
|
#endif
|
|
static void* StaticThreadProc(void* param);
|
|
|
|
protected:
|
|
void CreateThread();
|
|
void CloseThread();
|
|
|
|
public:
|
|
GSThread();
|
|
virtual ~GSThread();
|
|
};
|
|
|
|
class GSCritSec : public IGSLock
|
|
{
|
|
// XXX Do we really need a recursive mutex
|
|
// It would allow to use condition_variable instead of condition_variable_any
|
|
recursive_mutex *mutex_critsec;
|
|
|
|
public:
|
|
GSCritSec(bool recursive = true)
|
|
{
|
|
mutex_critsec = new recursive_mutex();
|
|
}
|
|
|
|
~GSCritSec()
|
|
{
|
|
delete(mutex_critsec);
|
|
}
|
|
|
|
void Lock()
|
|
{
|
|
mutex_critsec->lock();
|
|
}
|
|
|
|
bool TryLock()
|
|
{
|
|
return mutex_critsec->try_lock();
|
|
}
|
|
|
|
void Unlock()
|
|
{
|
|
mutex_critsec->unlock();
|
|
}
|
|
|
|
recursive_mutex& GetMutex() {return ref(*mutex_critsec);}
|
|
};
|
|
|
|
class GSCondVarLock : public GSCritSec
|
|
{
|
|
public:
|
|
GSCondVarLock() : GSCritSec(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
class GSCondVar : public IGSEvent
|
|
{
|
|
condition_variable_any *cond_var;
|
|
|
|
public:
|
|
GSCondVar()
|
|
{
|
|
cond_var = new condition_variable_any();
|
|
}
|
|
|
|
virtual ~GSCondVar()
|
|
{
|
|
delete(cond_var);
|
|
}
|
|
|
|
void Set()
|
|
{
|
|
cond_var->notify_one();
|
|
}
|
|
|
|
bool Wait(IGSLock* l)
|
|
{
|
|
cond_var->wait(((GSCondVarLock*)l)->GetMutex()); // Predicate is not useful, it is implicit in the loop
|
|
return 1; // Anyway this value is not used(and no way to get it from std::thread)
|
|
}
|
|
|
|
operator condition_variable_any* () {return cond_var;}
|
|
};
|
|
|
|
#endif
|
|
|
|
class GSAutoLock
|
|
{
|
|
IGSLock* m_lock;
|
|
|
|
public:
|
|
GSAutoLock(IGSLock* l) {(m_lock = l)->Lock();}
|
|
~GSAutoLock() {m_lock->Unlock();}
|
|
};
|
|
|
|
template<class T> class GSJobQueue : private GSThread
|
|
{
|
|
protected:
|
|
queue<T> m_queue;
|
|
volatile long m_count; // NOTE: it is the safest to have our own counter because m_queue.pop() might decrement its own before the last item runs out of its scope and gets destroyed (implementation dependent)
|
|
volatile bool m_exit;
|
|
IGSEvent* m_notempty;
|
|
IGSEvent* m_empty;
|
|
IGSLock* m_lock;
|
|
|
|
void ThreadProc()
|
|
{
|
|
m_lock->Lock();
|
|
|
|
while(true)
|
|
{
|
|
while(m_queue.empty())
|
|
{
|
|
m_notempty->Wait(m_lock);
|
|
|
|
if(m_exit) {m_lock->Unlock(); return;}
|
|
}
|
|
|
|
T& item = m_queue.front();
|
|
|
|
m_lock->Unlock();
|
|
|
|
Process(item);
|
|
|
|
m_lock->Lock();
|
|
|
|
m_queue.pop();
|
|
|
|
if(--m_count == 0)
|
|
{
|
|
m_empty->Set();
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
GSJobQueue()
|
|
: m_count(0)
|
|
, m_exit(false)
|
|
{
|
|
bool condvar = true;
|
|
|
|
#ifdef _WINDOWS
|
|
|
|
if(pInitializeConditionVariable == NULL)
|
|
{
|
|
condvar = false;
|
|
}
|
|
|
|
#endif
|
|
|
|
if(condvar)
|
|
{
|
|
m_notempty = new GSCondVar();
|
|
m_empty = new GSCondVar();
|
|
m_lock = new GSCondVarLock();
|
|
}
|
|
else
|
|
{
|
|
#ifdef _WINDOWS
|
|
m_notempty = new GSEvent();
|
|
m_empty = new GSEvent();
|
|
m_lock = new GSCritSec();
|
|
#endif
|
|
}
|
|
|
|
CreateThread();
|
|
}
|
|
|
|
virtual ~GSJobQueue()
|
|
{
|
|
m_exit = true;
|
|
|
|
m_notempty->Set();
|
|
|
|
CloseThread();
|
|
|
|
delete m_notempty;
|
|
delete m_empty;
|
|
delete m_lock;
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
ASSERT(m_count >= 0);
|
|
|
|
return m_count == 0;
|
|
}
|
|
|
|
void Push(const T& item)
|
|
{
|
|
m_lock->Lock();
|
|
|
|
m_queue.push(item);
|
|
|
|
if(m_count++ == 0)
|
|
{
|
|
m_notempty->Set();
|
|
}
|
|
|
|
m_lock->Unlock();
|
|
}
|
|
|
|
void Wait()
|
|
{
|
|
if(m_count > 0)
|
|
{
|
|
m_lock->Lock();
|
|
|
|
while(m_count != 0)
|
|
{
|
|
m_empty->Wait(m_lock);
|
|
}
|
|
|
|
ASSERT(m_queue.empty());
|
|
|
|
m_lock->Unlock();
|
|
}
|
|
}
|
|
|
|
virtual void Process(T& item) = 0;
|
|
};
|
|
|
|
// http://software.intel.com/en-us/blogs/2012/11/06/exploring-intel-transactional-synchronization-extensions-with-intel-software
|
|
#if 0
|
|
class TransactionScope
|
|
{
|
|
public:
|
|
class Lock
|
|
{
|
|
volatile long state;
|
|
|
|
public:
|
|
Lock()
|
|
: state(0)
|
|
{
|
|
}
|
|
|
|
void lock()
|
|
{
|
|
while(_InterlockedCompareExchange(&state, 1, 0) != 0)
|
|
{
|
|
do {_mm_pause();} while(state == 1);
|
|
}
|
|
}
|
|
|
|
void unlock()
|
|
{
|
|
_InterlockedExchange(&state, 0);
|
|
}
|
|
|
|
bool isLocked() const
|
|
{
|
|
return state == 1;
|
|
}
|
|
};
|
|
|
|
private:
|
|
Lock& fallBackLock;
|
|
|
|
TransactionScope();
|
|
|
|
public:
|
|
TransactionScope(Lock& fallBackLock_, int max_retries = 3)
|
|
: fallBackLock(fallBackLock_)
|
|
{
|
|
// The TSX (RTM/HLE) instructions on Intel AVX2 CPUs may either be
|
|
// absent or disabled (see errata HSD136 and specification change at
|
|
// http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/4th-gen-core-family-desktop-specification-update.pdf)
|
|
// This can cause builds for AVX2 CPUs to fail with GCC/Clang on Linux,
|
|
// so check that the RTM instructions are actually available.
|
|
#if (_M_SSE >= 0x501 && !defined(__GNUC__)) || defined(__RTM__)
|
|
|
|
int nretries = 0;
|
|
|
|
while(1)
|
|
{
|
|
++nretries;
|
|
|
|
unsigned status = _xbegin();
|
|
|
|
if(status == _XBEGIN_STARTED)
|
|
{
|
|
if(!fallBackLock.isLocked()) return;
|
|
|
|
_xabort(0xff);
|
|
}
|
|
|
|
if((status & _XABORT_EXPLICIT) && _XABORT_CODE(status) == 0xff && !(status & _XABORT_NESTED))
|
|
{
|
|
while(fallBackLock.isLocked()) _mm_pause();
|
|
}
|
|
else if(!(status & _XABORT_RETRY))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(nretries >= max_retries)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
fallBackLock.lock();
|
|
}
|
|
|
|
~TransactionScope()
|
|
{
|
|
if(fallBackLock.isLocked())
|
|
{
|
|
fallBackLock.unlock();
|
|
}
|
|
#if (_M_SSE >= 0x501 && !defined(__GNUC__)) || defined(__RTM__)
|
|
else
|
|
{
|
|
_xend();
|
|
}
|
|
#endif
|
|
}
|
|
};
|
|
#endif
|