Merge changes from The Witness.
This commit is contained in:
@ -14,6 +14,7 @@
|
||||
|
||||
#pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement)
|
||||
#pragma intrinsic(_InterlockedCompareExchange, _InterlockedExchange)
|
||||
//#pragma intrinsic(_InterlockedExchangeAdd64)
|
||||
|
||||
/*
|
||||
extern "C"
|
||||
@ -147,6 +148,11 @@ namespace nv {
|
||||
return (uint32)_InterlockedExchange((long *)value, (long)desired);
|
||||
}
|
||||
|
||||
inline uint32 atomicAdd(uint32 * value, uint32 value_to_add) {
|
||||
nvDebugCheck((intptr_t(value) & 3) == 0);
|
||||
return (uint32)_InterlockedExchangeAdd((long*)value, (long)value_to_add);
|
||||
}
|
||||
|
||||
#elif NV_CC_CLANG && (NV_OS_IOS || NV_OS_DARWIN)
|
||||
NV_COMPILER_CHECK(sizeof(uint32) == sizeof(long));
|
||||
|
||||
@ -177,14 +183,14 @@ namespace nv {
|
||||
inline uint32 atomicIncrement(uint32 * value)
|
||||
{
|
||||
nvDebugCheck((intptr_t(value) & 3) == 0);
|
||||
|
||||
|
||||
return __sync_add_and_fetch(value, 1);
|
||||
}
|
||||
|
||||
|
||||
inline uint32 atomicDecrement(uint32 * value)
|
||||
{
|
||||
nvDebugCheck((intptr_t(value) & 3) == 0);
|
||||
|
||||
|
||||
return __sync_sub_and_fetch(value, 1);
|
||||
}
|
||||
|
||||
@ -204,6 +210,12 @@ namespace nv {
|
||||
return __sync_lock_test_and_set(value, desired);
|
||||
}
|
||||
|
||||
inline uint32 atomicAdd(uint32 * value, uint32 value_to_add) {
|
||||
nvDebugCheck((intptr_t(value) & 3) == 0);
|
||||
return __sync_add_and_fetch(value, value_to_add);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#elif NV_CC_CLANG && POSH_CPU_STRONGARM
|
||||
NV_COMPILER_CHECK(sizeof(uint32) == sizeof(long));
|
||||
@ -288,6 +300,12 @@ namespace nv {
|
||||
// this is confusingly named, it doesn't actually do a test but always sets
|
||||
return __sync_lock_test_and_set(value, desired);
|
||||
}
|
||||
|
||||
inline uint32 atomicAdd(uint32 * value, uint32 value_to_add) {
|
||||
nvDebugCheck((intptr_t(value) & 3) == 0);
|
||||
return __sync_add_and_fetch(value, value_to_add);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#error "Atomics not implemented."
|
||||
|
@ -60,7 +60,7 @@ void Event::post() {
|
||||
|
||||
//ACS: move this after the unlock?
|
||||
if(m->wait_count>0) {
|
||||
pthread_cond_signal(&m->pt_cond);
|
||||
pthread_cond_signal(&m->pt_cond);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&m->pt_mutex);
|
||||
@ -71,7 +71,7 @@ void Event::wait() {
|
||||
|
||||
while(m->count==0) {
|
||||
m->wait_count++;
|
||||
pthread_cond_wait(&m->pt_cond, &m->pt_mutex);
|
||||
pthread_cond_wait(&m->pt_cond, &m->pt_mutex);
|
||||
m->wait_count--;
|
||||
}
|
||||
m->count--;
|
||||
|
@ -13,6 +13,11 @@
|
||||
|
||||
#endif // NV_OS
|
||||
|
||||
#if NV_USE_TELEMETRY
|
||||
#include <telemetry.h>
|
||||
extern HTELEMETRY tmContext;
|
||||
#endif
|
||||
|
||||
using namespace nv;
|
||||
|
||||
|
||||
@ -20,12 +25,17 @@ using namespace nv;
|
||||
|
||||
struct Mutex::Private {
|
||||
CRITICAL_SECTION mutex;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
|
||||
Mutex::Mutex () : m(new Private)
|
||||
Mutex::Mutex (const char * name) : m(new Private)
|
||||
{
|
||||
InitializeCriticalSection(&m->mutex);
|
||||
m->name = name;
|
||||
#if NV_USE_TELEMETRY
|
||||
tmLockName(tmContext, this, name);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~Mutex ()
|
||||
@ -35,16 +45,44 @@ Mutex::~Mutex ()
|
||||
|
||||
void Mutex::lock()
|
||||
{
|
||||
#if NV_USE_TELEMETRY
|
||||
TmU64 matcher;
|
||||
tmTryLockEx(tmContext, &matcher, 100/*0.1 ms*/, __FILE__, __LINE__, this, "blocked");
|
||||
#endif
|
||||
|
||||
EnterCriticalSection(&m->mutex);
|
||||
|
||||
#if NV_USE_TELEMETRY
|
||||
tmEndTryLockEx(tmContext, matcher, __FILE__, __LINE__, this, TMLR_SUCCESS);
|
||||
tmSetLockState(tmContext, this, TMLS_LOCKED, "acquired");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Mutex::tryLock()
|
||||
{
|
||||
#if NV_USE_TELEMETRY
|
||||
TmU64 matcher;
|
||||
tmTryLockEx(tmContext, &matcher, 100/*0.1 ms*/, __FILE__, __LINE__, this, "blocked");
|
||||
if (TryEnterCriticalSection(&m->mutex) != 0) {
|
||||
tmEndTryLockEx(tmContext, matcher, __FILE__, __LINE__, this, TMLR_SUCCESS);
|
||||
tmSetLockState(tmContext, this, TMLS_LOCKED, "acquired");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
tmEndTryLockEx(tmContext, matcher, __FILE__, __LINE__, this, TMLR_FAILED);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return TryEnterCriticalSection(&m->mutex) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
#if NV_USE_TELEMETRY
|
||||
tmSetLockState(tmContext, this, TMLS_RELEASED, "released");
|
||||
#endif
|
||||
|
||||
LeaveCriticalSection(&m->mutex);
|
||||
}
|
||||
|
||||
@ -52,12 +90,14 @@ void Mutex::unlock()
|
||||
|
||||
struct Mutex::Private {
|
||||
pthread_mutex_t mutex;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
|
||||
Mutex::Mutex () : m(new Private)
|
||||
Mutex::Mutex (const char * name) : m(new Private)
|
||||
{
|
||||
int result = pthread_mutex_init(&m->mutex , NULL);
|
||||
int result = pthread_mutex_init(&m->mutex, NULL);
|
||||
m->name = name;
|
||||
nvDebugCheck(result == 0);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace nv
|
||||
{
|
||||
NV_FORBID_COPY(Mutex);
|
||||
public:
|
||||
Mutex ();
|
||||
Mutex (const char * name);
|
||||
~Mutex ();
|
||||
|
||||
void lock();
|
||||
|
@ -38,7 +38,7 @@ ParallelFor::~ParallelFor() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ParallelFor::run(uint count) {
|
||||
void ParallelFor::run(uint count, bool calling_thread_process_work /*=false*/) {
|
||||
#if ENABLE_PARALLEL_FOR
|
||||
storeRelease(&this->count, count);
|
||||
|
||||
@ -48,6 +48,10 @@ void ParallelFor::run(uint count) {
|
||||
// Start threads.
|
||||
pool->start(worker, this);
|
||||
|
||||
if (calling_thread_process_work) {
|
||||
worker(this);
|
||||
}
|
||||
|
||||
// Wait for all threads to complete.
|
||||
pool->wait();
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace nv
|
||||
ParallelFor(ForTask * task, void * context);
|
||||
~ParallelFor();
|
||||
|
||||
void run(uint count);
|
||||
void run(uint count, bool calling_thread_process_work = false);
|
||||
|
||||
// Invariant:
|
||||
ForTask * task;
|
||||
|
@ -9,6 +9,12 @@
|
||||
#include <unistd.h> // usleep
|
||||
#endif
|
||||
|
||||
#if NV_USE_TELEMETRY
|
||||
#include <telemetry.h>
|
||||
extern HTELEMETRY tmContext;
|
||||
#endif
|
||||
|
||||
|
||||
using namespace nv;
|
||||
|
||||
struct Thread::Private
|
||||
@ -21,6 +27,7 @@ struct Thread::Private
|
||||
|
||||
ThreadFunc * func;
|
||||
void * arg;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
|
||||
@ -32,6 +39,39 @@ unsigned long __stdcall threadFunc(void * arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// SetThreadName implementation from msdn:
|
||||
// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
||||
|
||||
const DWORD MS_VC_EXCEPTION=0x406D1388;
|
||||
|
||||
#pragma pack(push,8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
static void setThreadName(DWORD dwThreadID, const char* threadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = threadName;
|
||||
info.dwThreadID = dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif NV_OS_USE_PTHREAD
|
||||
|
||||
extern "C" void * threadFunc(void * arg) {
|
||||
@ -46,6 +86,13 @@ extern "C" void * threadFunc(void * arg) {
|
||||
Thread::Thread() : p(new Private)
|
||||
{
|
||||
p->thread = 0;
|
||||
p->name = NULL;
|
||||
}
|
||||
|
||||
Thread::Thread(const char * const name) : p(new Private)
|
||||
{
|
||||
p->thread = 0;
|
||||
p->name = name;
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
@ -59,9 +106,20 @@ void Thread::start(ThreadFunc * func, void * arg)
|
||||
p->arg = arg;
|
||||
|
||||
#if NV_OS_WIN32
|
||||
p->thread = CreateThread(NULL, 0, threadFunc, p.ptr(), 0, NULL);
|
||||
DWORD threadId;
|
||||
p->thread = CreateThread(NULL, 0, threadFunc, p.ptr(), 0, &threadId);
|
||||
//p->thread = (HANDLE)_beginthreadex (0, 0, threadFunc, p.ptr(), 0, NULL); // @@ So that we can call CRT functions...
|
||||
nvDebugCheck(p->thread != NULL);
|
||||
setThreadName(threadId, p->name);
|
||||
#if NV_USE_TELEMETRY
|
||||
tmThreadName(tmContext, threadId, p->name);
|
||||
#endif
|
||||
#elif NV_OS_ORBIS
|
||||
int ret = scePthreadCreate(&p->thread, NULL, threadFunc, p.ptr(), p->name ? p->name : "nv::Thread");
|
||||
nvDebugCheck(ret == 0);
|
||||
// use any non-system core
|
||||
scePthreadSetaffinity(p->thread, 0x3F);
|
||||
scePthreadSetprio(p->thread, (SCE_KERNEL_PRIO_FIFO_DEFAULT + SCE_KERNEL_PRIO_FIFO_HIGHEST) / 2);
|
||||
#elif NV_OS_USE_PTHREAD
|
||||
int result = pthread_create(&p->thread, NULL, threadFunc, p.ptr());
|
||||
nvDebugCheck(result == 0);
|
||||
|
@ -17,6 +17,7 @@ namespace nv
|
||||
NV_FORBID_COPY(Thread);
|
||||
public:
|
||||
Thread();
|
||||
Thread(const char * const name);
|
||||
~Thread();
|
||||
|
||||
void start(ThreadFunc * func, void * arg);
|
||||
|
@ -14,7 +14,7 @@
|
||||
using namespace nv;
|
||||
|
||||
#if PROTECT_THREAD_POOL
|
||||
Mutex s_pool_mutex;
|
||||
Mutex s_pool_mutex("thread pool");
|
||||
#endif
|
||||
|
||||
AutoPtr<ThreadPool> s_pool;
|
||||
|
@ -12,7 +12,7 @@
|
||||
// The thread pool creates one worker thread for each physical core.
|
||||
// The threads are idle waiting for their start events so that they do not consume any resources while inactive.
|
||||
// The thread pool runs the same function in all worker threads, the idea is to use this as the foundation of a custom task scheduler.
|
||||
// When the thread pool starts, the main thread continues running, but the common use case is to inmmediately wait of the termination events of the worker threads.
|
||||
// When the thread pool starts, the main thread continues running, but the common use case is to inmmediately wait for the termination events of the worker threads.
|
||||
// @@ The start and wait methods could probably be merged.
|
||||
|
||||
namespace nv {
|
||||
|
@ -72,3 +72,10 @@ uint nv::hardwareThreadCount() {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint nv::threadId() {
|
||||
#if NV_OS_WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return 0; // @@
|
||||
#endif
|
||||
}
|
@ -90,6 +90,8 @@ namespace nv
|
||||
void shutWorkers();
|
||||
void setWorkerFunction(void * func);
|
||||
|
||||
uint threadId();
|
||||
|
||||
} // nv namespace
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user