Massive Poo storm, had to take shelter. Hacks, fixes, etc etc

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5421 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Sonicadvance1 2010-04-29 13:46:20 +00:00
parent 3103b920b6
commit 81f06220ce
2 changed files with 567 additions and 679 deletions

View File

@ -17,7 +17,6 @@
#include "Setup.h" #include "Setup.h"
#include "Thread.h" #include "Thread.h"
#include "Atomic.h"
#include "Common.h" #include "Common.h"
#ifdef USE_BEGINTHREADEX #ifdef USE_BEGINTHREADEX
@ -27,24 +26,24 @@
namespace Common namespace Common
{ {
int Thread::CurrentId() int Thread::CurrentId()
{ {
#ifdef _WIN32 #ifdef _WIN32
return GetCurrentThreadId(); return GetCurrentThreadId();
#else #else
return 0; return 0;
#endif #endif
} }
#ifdef _WIN32 #ifdef _WIN32
void InitThreading() void InitThreading()
{ {
// Nothing to do in Win32 build. // Nothing to do in Win32 build.
} }
CriticalSection::CriticalSection(int spincount) CriticalSection::CriticalSection(int spincount)
{ {
if (spincount) if (spincount)
{ {
if (!InitializeCriticalSectionAndSpinCount(&section, spincount)) if (!InitializeCriticalSectionAndSpinCount(&section, spincount))
@ -54,45 +53,45 @@ CriticalSection::CriticalSection(int spincount)
{ {
InitializeCriticalSection(&section); InitializeCriticalSection(&section);
} }
} }
CriticalSection::~CriticalSection() CriticalSection::~CriticalSection()
{ {
DeleteCriticalSection(&section); DeleteCriticalSection(&section);
} }
void CriticalSection::Enter() void CriticalSection::Enter()
{ {
EnterCriticalSection(&section); EnterCriticalSection(&section);
} }
bool CriticalSection::TryEnter() bool CriticalSection::TryEnter()
{ {
return TryEnterCriticalSection(&section) ? true : false; return TryEnterCriticalSection(&section) ? true : false;
} }
void CriticalSection::Leave() void CriticalSection::Leave()
{ {
LeaveCriticalSection(&section); LeaveCriticalSection(&section);
} }
Thread::Thread(ThreadFunc function, void* arg) Thread::Thread(ThreadFunc function, void* arg)
: m_hThread(NULL), m_threadId(0) : m_hThread(NULL), m_threadId(0)
{ {
#ifdef USE_BEGINTHREADEX #ifdef USE_BEGINTHREADEX
m_hThread = (HANDLE)_beginthreadex(NULL, 0, function, arg, 0, &m_threadId); m_hThread = (HANDLE)_beginthreadex(NULL, 0, function, arg, 0, &m_threadId);
#else #else
m_hThread = CreateThread(NULL, 0, function, arg, 0, &m_threadId); m_hThread = CreateThread(NULL, 0, function, arg, 0, &m_threadId);
#endif #endif
} }
Thread::~Thread() Thread::~Thread()
{ {
WaitForDeath(); WaitForDeath();
} }
DWORD Thread::WaitForDeath(const int iWait) DWORD Thread::WaitForDeath(const int iWait)
{ {
if (m_hThread) if (m_hThread)
{ {
DWORD Wait = WaitForSingleObject(m_hThread, iWait); DWORD Wait = WaitForSingleObject(m_hThread, iWait);
@ -101,66 +100,66 @@ DWORD Thread::WaitForDeath(const int iWait)
return Wait; return Wait;
} }
return NULL; return NULL;
} }
void Thread::SetAffinity(int mask) void Thread::SetAffinity(int mask)
{ {
SetThreadAffinityMask(m_hThread, mask); SetThreadAffinityMask(m_hThread, mask);
} }
void Thread::SetPriority(int priority) void Thread::SetPriority(int priority)
{ {
SetThreadPriority(m_hThread, priority); SetThreadPriority(m_hThread, priority);
} }
void Thread::SetCurrentThreadAffinity(int mask) void Thread::SetCurrentThreadAffinity(int mask)
{ {
SetThreadAffinityMask(GetCurrentThread(), mask); SetThreadAffinityMask(GetCurrentThread(), mask);
} }
bool Thread::IsCurrentThread() bool Thread::IsCurrentThread()
{ {
return GetCurrentThreadId() == m_threadId; return GetCurrentThreadId() == m_threadId;
} }
EventEx::EventEx() EventEx::EventEx()
{ {
InterlockedExchange(&m_Lock, 1); InterlockedExchange(&m_Lock, 1);
} }
void EventEx::Init() void EventEx::Init()
{ {
InterlockedExchange(&m_Lock, 1); InterlockedExchange(&m_Lock, 1);
} }
void EventEx::Shutdown() void EventEx::Shutdown()
{ {
InterlockedExchange(&m_Lock, 0); InterlockedExchange(&m_Lock, 0);
} }
void EventEx::Set() void EventEx::Set()
{ {
InterlockedExchange(&m_Lock, 0); InterlockedExchange(&m_Lock, 0);
} }
void EventEx::Spin() void EventEx::Spin()
{ {
while (InterlockedCompareExchange(&m_Lock, 1, 0)) while (InterlockedCompareExchange(&m_Lock, 1, 0))
// This only yields when there is a runnable thread on this core // This only yields when there is a runnable thread on this core
// If not, spin // If not, spin
SwitchToThread(); SwitchToThread();
} }
void EventEx::Wait() void EventEx::Wait()
{ {
while (InterlockedCompareExchange(&m_Lock, 1, 0)) while (InterlockedCompareExchange(&m_Lock, 1, 0))
// This directly enters Ring0 and enforces a sleep about 15ms // This directly enters Ring0 and enforces a sleep about 15ms
SleepCurrentThread(1); SleepCurrentThread(1);
} }
bool EventEx::MsgWait() bool EventEx::MsgWait()
{ {
while (InterlockedCompareExchange(&m_Lock, 1, 0)) while (InterlockedCompareExchange(&m_Lock, 1, 0))
{ {
MSG msg; MSG msg;
@ -174,43 +173,43 @@ bool EventEx::MsgWait()
SleepCurrentThread(1); SleepCurrentThread(1);
} }
return true; return true;
} }
// Regular same thread loop based waiting // Regular same thread loop based waiting
Event::Event() Event::Event()
{ {
m_hEvent = 0; m_hEvent = 0;
} }
void Event::Init() void Event::Init()
{ {
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
} }
void Event::Shutdown() void Event::Shutdown()
{ {
CloseHandle(m_hEvent); CloseHandle(m_hEvent);
m_hEvent = 0; m_hEvent = 0;
} }
void Event::Set() void Event::Set()
{ {
SetEvent(m_hEvent); SetEvent(m_hEvent);
} }
bool Event::Wait(const u32 timeout) bool Event::Wait(const u32 timeout)
{ {
return WaitForSingleObject(m_hEvent, timeout) != WAIT_OBJECT_0; return WaitForSingleObject(m_hEvent, timeout) != WAIT_OBJECT_0;
} }
inline HRESULT MsgWaitForSingleObject(HANDLE handle, DWORD timeout) inline HRESULT MsgWaitForSingleObject(HANDLE handle, DWORD timeout)
{ {
return MsgWaitForMultipleObjects(1, &handle, FALSE, timeout, 0); return MsgWaitForMultipleObjects(1, &handle, FALSE, timeout, 0);
} }
void Event::MsgWait() void Event::MsgWait()
{ {
// Adapted from MSDN example http://msdn.microsoft.com/en-us/library/ms687060.aspx // Adapted from MSDN example http://msdn.microsoft.com/en-us/library/ms687060.aspx
while (true) while (true)
{ {
@ -247,31 +246,31 @@ void Event::MsgWait()
return; return;
} }
} }
} }
// Supporting functions // Supporting functions
void SleepCurrentThread(int ms) void SleepCurrentThread(int ms)
{ {
Sleep(ms); Sleep(ms);
} }
typedef struct tagTHREADNAME_INFO typedef struct tagTHREADNAME_INFO
{ {
DWORD dwType; // must be 0x1000 DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space) LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread) DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO; } THREADNAME_INFO;
// Usage: SetThreadName (-1, "MainThread"); // Usage: SetThreadName (-1, "MainThread");
// //
// Sets the debugger-visible name of the current thread. // Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick. // Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
// This is implemented much nicer in upcoming msvc++, see: // This is implemented much nicer in upcoming msvc++, see:
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
void SetCurrentThreadName(const TCHAR* szThreadName) void SetCurrentThreadName(const TCHAR* szThreadName)
{ {
THREADNAME_INFO info; THREADNAME_INFO info;
info.dwType = 0x1000; info.dwType = 0x1000;
#ifdef UNICODE #ifdef UNICODE
@ -298,73 +297,49 @@ void SetCurrentThreadName(const TCHAR* szThreadName)
} }
__except(EXCEPTION_CONTINUE_EXECUTION) __except(EXCEPTION_CONTINUE_EXECUTION)
{} {}
} }
#else // !WIN32, so must be POSIX threads #else // !WIN32, so must be POSIX threads
pthread_key_t threadname_key; pthread_key_t threadname_key;
CriticalSection::CriticalSection(int spincount_unused) CriticalSection::CriticalSection(int spincount_unused)
{ {
#ifdef __APPLE__
lock = OS_SPINLOCK_INIT;
#else
pthread_mutex_init(&mutex, NULL); pthread_mutex_init(&mutex, NULL);
#endif }
}
CriticalSection::~CriticalSection() CriticalSection::~CriticalSection()
{ {
#ifndef __APPLE__
pthread_mutex_destroy(&mutex); pthread_mutex_destroy(&mutex);
#endif }
}
#ifndef __APPLE__
void CriticalSection::Enter() void CriticalSection::Enter()
{ {
int ret = pthread_mutex_lock(&mutex); int ret = pthread_mutex_lock(&mutex);
if (ret) ERROR_LOG(COMMON, "%s: pthread_mutex_lock(%p) failed: %s\n", if (ret) ERROR_LOG(COMMON, "%s: pthread_mutex_lock(%p) failed: %s\n",
__FUNCTION__, &mutex, strerror(ret)); __FUNCTION__, &mutex, strerror(ret));
} }
bool CriticalSection::TryEnter() bool CriticalSection::TryEnter()
{ {
return(!pthread_mutex_trylock(&mutex)); return(!pthread_mutex_trylock(&mutex));
} }
void CriticalSection::Leave() void CriticalSection::Leave()
{ {
int ret = pthread_mutex_unlock(&mutex); int ret = pthread_mutex_unlock(&mutex);
if (ret) ERROR_LOG(COMMON, "%s: pthread_mutex_unlock(%p) failed: %s\n", if (ret) ERROR_LOG(COMMON, "%s: pthread_mutex_unlock(%p) failed: %s\n",
__FUNCTION__, &mutex, strerror(ret)); __FUNCTION__, &mutex, strerror(ret));
} }
#else
void CriticalSection::Enter()
{
OSSpinLockLock(&lock);
}
bool CriticalSection::TryEnter() Thread::Thread(ThreadFunc function, void* arg)
{
return(!OSSpinLockTry(&lock));
}
void CriticalSection::Leave()
{
OSSpinLockUnlock(&lock);
}
#endif
Thread::Thread(ThreadFunc function, void* arg)
: thread_id(0) : thread_id(0)
{ {
pthread_attr_t attr; pthread_attr_t attr;
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1024 * 1024); pthread_attr_setstacksize(&attr, 1024 * 1024);
@ -373,17 +348,17 @@ Thread::Thread(ThreadFunc function, void* arg)
__FUNCTION__, &thread_id, &attr, function, arg, strerror(ret)); __FUNCTION__, &thread_id, &attr, function, arg, strerror(ret));
INFO_LOG(COMMON, "created new thread %lu (func=%p, arg=%p)\n", thread_id, function, arg); INFO_LOG(COMMON, "created new thread %lu (func=%p, arg=%p)\n", thread_id, function, arg);
} }
Thread::~Thread() Thread::~Thread()
{ {
WaitForDeath(); WaitForDeath();
} }
void Thread::WaitForDeath() void Thread::WaitForDeath()
{ {
if (thread_id) if (thread_id)
{ {
void* exit_status; void* exit_status;
@ -393,11 +368,11 @@ void Thread::WaitForDeath()
ERROR_LOG(COMMON, "thread %lu exited with status %d\n", thread_id, *(int *)exit_status); ERROR_LOG(COMMON, "thread %lu exited with status %d\n", thread_id, *(int *)exit_status);
thread_id = 0; thread_id = 0;
} }
} }
void Thread::SetAffinity(int mask) void Thread::SetAffinity(int mask)
{ {
// This is non-standard // This is non-standard
#ifdef __linux__ #ifdef __linux__
cpu_set_t cpu_set; cpu_set_t cpu_set;
@ -410,10 +385,10 @@ void Thread::SetAffinity(int mask)
pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set); pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set);
#endif #endif
} }
void Thread::SetCurrentThreadAffinity(int mask) void Thread::SetCurrentThreadAffinity(int mask)
{ {
#ifdef __linux__ #ifdef __linux__
cpu_set_t cpu_set; cpu_set_t cpu_set;
CPU_ZERO(&cpu_set); CPU_ZERO(&cpu_set);
@ -425,14 +400,14 @@ void Thread::SetCurrentThreadAffinity(int mask)
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
#endif #endif
} }
bool Thread::IsCurrentThread() bool Thread::IsCurrentThread()
{ {
return pthread_equal(pthread_self(), thread_id) != 0; return pthread_equal(pthread_self(), thread_id) != 0;
} }
void InitThreading() { void InitThreading() {
static int thread_init_done = 0; static int thread_init_done = 0;
if (thread_init_done) if (thread_init_done)
return; return;
@ -441,16 +416,16 @@ void InitThreading() {
perror("Unable to create thread name key: "); perror("Unable to create thread name key: ");
thread_init_done++; thread_init_done++;
} }
void SleepCurrentThread(int ms) void SleepCurrentThread(int ms)
{ {
usleep(1000 * ms); usleep(1000 * ms);
} }
void SetCurrentThreadName(const TCHAR* szThreadName) void SetCurrentThreadName(const TCHAR* szThreadName)
{ {
char *name = strdup(szThreadName); char *name = strdup(szThreadName);
// pthread_setspecific returns 0 on success // pthread_setspecific returns 0 on success
// free the string from strdup if fails // free the string from strdup if fails
@ -460,103 +435,31 @@ void SetCurrentThreadName(const TCHAR* szThreadName)
if(!pthread_setspecific(threadname_key, name)) if(!pthread_setspecific(threadname_key, name))
free(name); free(name);
INFO_LOG(COMMON, "%s(%s)\n", __FUNCTION__, szThreadName); INFO_LOG(COMMON, "%s(%s)\n", __FUNCTION__, szThreadName);
} }
Event::Event() Event::Event()
{ {
#ifdef __APPLE__
lock = OS_SPINLOCK_INIT;
event_ = 0;
#endif
is_set_ = false; is_set_ = false;
} }
void Event::Init() void Event::Init()
{ {
#ifndef __APPLE__
pthread_cond_init(&event_, 0); pthread_cond_init(&event_, 0);
pthread_mutex_init(&mutex_, 0); pthread_mutex_init(&mutex_, 0);
#else }
lock = OS_SPINLOCK_INIT;
event_ = 0;
#endif
}
void Event::Shutdown() void Event::Shutdown()
{ {
#ifndef __APPLE__
pthread_mutex_destroy(&mutex_); pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&event_); pthread_cond_destroy(&event_);
#endif
}
#ifdef __APPLE__
void Event::Set()
{
OSSpinLockLock(&lock);
if (!is_set_)
{
is_set_ = true;
Common::AtomicStore(event_, 1);
} }
OSSpinLockUnlock(&lock);
}
void Event::Set()
bool Event::Wait(const u32 timeout)
{
bool timedout = false;
struct timespec wait;
if (timeout != INFINITE)
{ {
struct timeval now;
gettimeofday(&now, NULL);
memset(&wait, 0, sizeof(wait));
//TODO: timespec also has nanoseconds, but do we need them?
//as consequence, waiting is limited to seconds for now.
//the following just looks ridiculous, and probably fails for
//values 429 < ms <= 999 since it overflows the long.
//wait.tv_nsec = (now.tv_usec + (timeout % 1000) * 1000) * 1000);
wait.tv_sec = now.tv_sec + (timeout / 1000);
}
int Slept = 0;
while (!timedout)
{
if (timeout == INFINITE)
{
if(Common::AtomicLoad(event_) == 1)
break;
}
else
{
if(Slept >= wait.tv_sec * 1000000)
timedout = true;
else
if(Common::AtomicLoad(event_) == 1)
break;
}
usleep(250);
Slept += 250;
}
OSSpinLockLock(&lock);
is_set_ = false;
Common::AtomicStore(event_, 0);
OSSpinLockUnlock(&lock);
return timedout;
}
#else
void Event::Set()
{
pthread_mutex_lock(&mutex_); pthread_mutex_lock(&mutex_);
if (!is_set_) if (!is_set_)
@ -566,11 +469,11 @@ void Event::Set()
} }
pthread_mutex_unlock(&mutex_); pthread_mutex_unlock(&mutex_);
} }
bool Event::Wait(const u32 timeout) bool Event::Wait(const u32 timeout)
{ {
bool timedout = false; bool timedout = false;
struct timespec wait; struct timespec wait;
pthread_mutex_lock(&mutex_); pthread_mutex_lock(&mutex_);
@ -605,8 +508,7 @@ bool Event::Wait(const u32 timeout)
pthread_mutex_unlock(&mutex_); pthread_mutex_unlock(&mutex_);
return timedout; return timedout;
} }
#endif
#endif #endif

View File

@ -47,10 +47,6 @@
#error unsupported platform (no pthreads?) #error unsupported platform (no pthreads?)
#endif #endif
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#endif
#endif #endif
// Don't include common.h here as it will break LogManager // Don't include common.h here as it will break LogManager
@ -60,9 +56,9 @@
// This may not be defined outside _WIN32 // This may not be defined outside _WIN32
#ifndef _WIN32 #ifndef _WIN32
#ifndef INFINITE #ifndef INFINITE
#define INFINITE 0xffffffff #define INFINITE 0xffffffff
#endif #endif
//for gettimeofday and struct time(val|spec) //for gettimeofday and struct time(val|spec)
#include <sys/time.h> #include <sys/time.h>
@ -73,45 +69,41 @@
namespace Common namespace Common
{ {
class CriticalSection class CriticalSection
{ {
#ifdef _WIN32 #ifdef _WIN32
CRITICAL_SECTION section; CRITICAL_SECTION section;
#else #else
#ifdef _POSIX_THREADS #ifdef _POSIX_THREADS
#ifdef __APPLE__
OSSpinLock lock;
#else
pthread_mutex_t mutex; pthread_mutex_t mutex;
#endif
#endif #endif
#endif #endif
public: public:
CriticalSection(int spincount = 1000); CriticalSection(int spincount = 1000);
~CriticalSection(); ~CriticalSection();
void Enter(); void Enter();
bool TryEnter(); bool TryEnter();
void Leave(); void Leave();
}; };
#ifdef _WIN32 #ifdef _WIN32
#ifdef USE_BEGINTHREADEX #ifdef USE_BEGINTHREADEX
typedef unsigned (__stdcall *ThreadFunc)(void* arg); typedef unsigned (__stdcall *ThreadFunc)(void* arg);
#else #else
typedef DWORD (WINAPI *ThreadFunc)(void* arg); typedef DWORD (WINAPI *ThreadFunc)(void* arg);
#endif #endif
#else #else
typedef void* (*ThreadFunc)(void* arg); typedef void* (*ThreadFunc)(void* arg);
#endif #endif
class Thread class Thread
{ {
public: public:
Thread(ThreadFunc entry, void* arg); Thread(ThreadFunc entry, void* arg);
~Thread(); ~Thread();
@ -126,7 +118,7 @@ public:
void WaitForDeath(); void WaitForDeath();
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
@ -144,16 +136,16 @@ private:
#endif #endif
#endif #endif
}; };
#ifdef _WIN32 #ifdef _WIN32
// Event(WaitForSingleObject) is too expensive // Event(WaitForSingleObject) is too expensive
// as it always enters Ring0 regardless of the state of lock // as it always enters Ring0 regardless of the state of lock
// This EventEx will try to stay in Ring3 as much as possible // This EventEx will try to stay in Ring3 as much as possible
// If the lock can be obtained in the first time, Ring0 won't be entered at all // If the lock can be obtained in the first time, Ring0 won't be entered at all
class EventEx class EventEx
{ {
public: public:
EventEx(); EventEx();
void Init(); void Init();
void Shutdown(); void Shutdown();
@ -164,19 +156,18 @@ public:
void Wait(); void Wait();
// Wait with message processing and sleep // Wait with message processing and sleep
bool MsgWait(); bool MsgWait();
private: private:
volatile long m_Lock; volatile long m_Lock;
}; };
#else #else
// TODO: implement for Linux // TODO: implement for Linux
#define EventEx Event #define EventEx Event
#endif #endif
class Event class Event
{ {
public: public:
Event(); Event();
void Init(); void Init();
void Shutdown(); void Shutdown();
@ -190,7 +181,7 @@ public:
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
HANDLE m_hEvent; HANDLE m_hEvent;
@ -205,35 +196,30 @@ private:
bool is_set_; bool is_set_;
#ifdef _POSIX_THREADS #ifdef _POSIX_THREADS
#ifdef __APPLE__
OSSpinLock lock;
u32 event_;
#else
pthread_cond_t event_; pthread_cond_t event_;
pthread_mutex_t mutex_; pthread_mutex_t mutex_;
#endif #endif
#endif
#endif #endif
}; };
void InitThreading(); void InitThreading();
void SleepCurrentThread(int ms); void SleepCurrentThread(int ms);
// YieldCPU: This function is only effective on HyperThreading CPU // YieldCPU: This function is only effective on HyperThreading CPU
// Use this function during a spin-wait to make the current thread // Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient // relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls. // than using events because event functions use kernel calls.
inline void YieldCPU() inline void YieldCPU()
{ {
#ifdef _WIN32 #ifdef _WIN32
YieldProcessor(); YieldProcessor();
#elif defined(_M_IX86) || defined(_M_X64) #elif defined(_M_IX86) || defined(_M_X64)
_mm_pause(); _mm_pause();
#endif #endif
} }
void SetCurrentThreadName(const char *name); void SetCurrentThreadName(const char *name);
} // namespace Common } // namespace Common