diff options
Diffstat (limited to 'LibOVRKernel/Src/Kernel/OVR_ThreadsWinAPI.cpp')
-rw-r--r-- | LibOVRKernel/Src/Kernel/OVR_ThreadsWinAPI.cpp | 1150 |
1 files changed, 1150 insertions, 0 deletions
diff --git a/LibOVRKernel/Src/Kernel/OVR_ThreadsWinAPI.cpp b/LibOVRKernel/Src/Kernel/OVR_ThreadsWinAPI.cpp new file mode 100644 index 0000000..8cba464 --- /dev/null +++ b/LibOVRKernel/Src/Kernel/OVR_ThreadsWinAPI.cpp @@ -0,0 +1,1150 @@ +/************************************************************************************ + +Filename : OVR_ThreadsWinAPI.cpp +Platform : WinAPI +Content : Windows specific thread-related (safe) functionality +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "OVR_Threads.h" +#include "OVR_Hash.h" +#include "OVR_Log.h" +#include "OVR_Timer.h" + +#ifdef OVR_ENABLE_THREADS + +// For _beginthreadex / _endtheadex +#include <process.h> + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// *** Internal Mutex implementation class + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + HANDLE hMutexOrSemaphore; + bool Recursive; + volatile unsigned LockCount; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); +}; + +// *** Constructor/destructor +MutexImpl::MutexImpl(bool recursive) +{ + Recursive = recursive; + LockCount = 0; +#if defined(OVR_OS_WIN32) // Older versions of Windows don't support CreateSemaphoreEx, so stick with CreateSemaphore for portability. + hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); +#else + // No CreateSemaphore() call, so emulate it. + hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphoreEx(NULL, 1, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); +#endif +} +MutexImpl::~MutexImpl() +{ + CloseHandle(hMutexOrSemaphore); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) + return; + LockCount++; +} + +bool MutexImpl::TryLock() +{ + DWORD ret; + if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) + return 0; + LockCount++; + return 1; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_UNUSED(pmutex); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + // Release mutex + if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : + ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) + { + // This used to call Wait handlers if lockCount == 0. + } +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (!TryLock()) + return 1; + Unlock(pmutex); + return 0; +} + +/* +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} +*/ + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + pImpl = new MutexImpl(recursive); +} +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + +//----------------------------------------------------------------------------------- +// ***** Win32 Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + // Event pool entries for extra events + struct EventPoolEntry : public NewOverrideBase + { + HANDLE hEvent; + EventPoolEntry *pNext; + EventPoolEntry *pPrev; + }; + + Lock WaitQueueLoc; + // Stores free events that can be used later + EventPoolEntry * pFreeEventList; + + // A queue of waiting objects to be signaled + EventPoolEntry* pQueueHead; + EventPoolEntry* pQueueTail; + + // Allocation functions for free events + EventPoolEntry* GetNewEvent(); + void ReleaseEvent(EventPoolEntry* pevent); + + // Queue operations + void QueuePush(EventPoolEntry* pentry); + EventPoolEntry* QueuePop(); + void QueueFindAndRemove(EventPoolEntry* pentry); + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-acqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + + +WaitConditionImpl::WaitConditionImpl() +{ + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + +WaitConditionImpl::~WaitConditionImpl() +{ + // Free all the resources + EventPoolEntry* p = pFreeEventList; + EventPoolEntry* pentry; + + while(p) + { + // Move to next + pentry = p; + p = p->pNext; + // Delete old + ::CloseHandle(pentry->hEvent); + delete pentry; + } + // Shouldn't we also consider the queue? + + // To be safe + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + + +// Allocation functions for free events +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() +{ + EventPoolEntry* pentry; + + // If there are any free nodes, use them + if (pFreeEventList) + { + pentry = pFreeEventList; + pFreeEventList = pFreeEventList->pNext; + } + else + { + // Allocate a new node + pentry = new EventPoolEntry; + pentry->pNext = 0; + pentry->pPrev = 0; + // Non-signaled manual event + pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); + } + + return pentry; +} + +void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) +{ + // Mark event as non-signaled + ::ResetEvent(pevent->hEvent); + // And add it to free pool + pevent->pNext = pFreeEventList; + pevent->pPrev = 0; + pFreeEventList = pevent; +} + +// Queue operations +void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) +{ + // Items already exist? Just add to tail + if (pQueueTail) + { + pentry->pPrev = pQueueTail; + pQueueTail->pNext = pentry; + pentry->pNext = 0; + pQueueTail = pentry; + } + else + { + // No items in queue + pentry->pNext = + pentry->pPrev = 0; + pQueueHead = + pQueueTail = pentry; + } +} + +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() +{ + EventPoolEntry* pentry = pQueueHead; + + // No items, null pointer + if (pentry) + { + // More items after this one? just grab the first item + if (pQueueHead->pNext) + { + pQueueHead = pentry->pNext; + pQueueHead->pPrev = 0; + } + else + { + // Last item left + pQueueTail = + pQueueHead = 0; + } + } + return pentry; +} + +void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) +{ + // Do an exhaustive search looking for an entry + EventPoolEntry* p = pQueueHead; + + while(p) + { + // Entry found? Remove. + if (p == pentry) + { + + // Remove the node form the list + // Prev link + if (pentry->pPrev) + pentry->pPrev->pNext = pentry->pNext; + else + pQueueHead = pentry->pNext; + // Next link + if (pentry->pNext) + pentry->pNext->pPrev = pentry->pPrev; + else + pQueueTail = pentry->pPrev; + // Done + return; + } + + // Move to next item + p = p->pNext; + } +} + + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 0; + unsigned i; + unsigned lockCount = pmutex->pImpl->LockCount; + EventPoolEntry* pentry; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + // Add an object to the wait queue + WaitQueueLoc.DoLock(); + QueuePush(pentry = GetNewEvent()); + WaitQueueLoc.Unlock(); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(i=0; i<lockCount; i++) + ::ReleaseMutex(pmutex->pImpl->hMutexOrSemaphore); + } + else + { + pmutex->pImpl->LockCount = 0; + ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). However, + // if notify() comes in at this point in the other thread it will set our + // corresponding event so wait will just fall through, as expected. + + // Block and wait on the event + DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); + /* +repeat_wait: + DWORD waitResult = + + ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, + QS_ALLINPUT); + */ + + WaitQueueLoc.DoLock(); + switch(waitResult) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + result = 1; + // Wait was successful, therefore the event entry should already be removed + // So just add entry back to a free list + ReleaseEvent(pentry); + break; + /* + case WAIT_OBJECT_0 + 1: + // Messages in WINDOWS queue + { + MSG msg; + PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); + WaitQueueLoc.Unlock(); + goto repeat_wait; + } + break; */ + default: + // Timeout, our entry should still be in a queue + QueueFindAndRemove(pentry); + ReleaseEvent(pentry); + } + WaitQueueLoc.Unlock(); + + // Re-aquire the mutex + for(i=0; i<lockCount; i++) + pmutex->DoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop last entry & signal it + EventPoolEntry* pentry = QueuePop(); + if (pentry) + ::SetEvent(pentry->hEvent); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop and signal all events + // NOTE : There is no need to release the events, it's the waiters job to do so + EventPoolEntry* pentry = QueuePop(); + while (pentry) + { + ::SetEvent(pentry->hEvent); + pentry = QueuePop(); + } +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +// Wait without a mutex +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + + +//----------------------------------------------------------------------------------- +// ***** Thread Class + +// Per-thread variable +// MA: Don't use TLS for now - portability issues with DLLs, etc. +/* +#if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) +__declspec(thread) Thread* pCurrentThread = 0; +#else +#pragma data_seg(".tls$") +__declspec(thread) Thread* pCurrentThread = 0; +#pragma data_seg(".rwdata") +#endif +*/ + +// *** Thread constructors. + +Thread::Thread(size_t stackSize, int processor) +{ + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, size_t stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + IdValue = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); + +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + CleanupSystemThread(); + ThreadHandle = 0; +} + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + if (!ThreadFunction) + return 0; + + int ret = ThreadFunction(this, UserHandle); + + return ret; +} + +void Thread::OnExit() +{ +} + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (uint32_t)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + +// *** ThreadList - used to tack all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + size_t operator()(const Thread* ptr) + { + return (((size_t)ptr) >> 6) ^ (size_t)ptr; + } + }; + + HashSet<Thread*, ThreadHashOp> ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + ThreadId RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(GetCurrentThreadId() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = GetCurrentThreadId(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (uint32_t)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + + return ExitCode; +} + + + +/* MA: Don't use TLS for now. + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (uint32_t) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} +// Join thread +bool Thread::Join(int maxWaitMs) const +{ + // If polling, + if (maxWaitMs == 0) + { + // Just return if finished + return IsFinished(); + } + // If waiting forever, + else if (maxWaitMs > 0) + { + // Try waiting once + WaitForSingleObject(ThreadHandle, maxWaitMs); + + // Return if the wait succeeded + return IsFinished(); + } + + // While not finished, + while (!IsFinished()) + { + // Wait for the thread handle to signal + WaitForSingleObject(ThreadHandle, INFINITE); + } + + return true; +} + + +// ***** Thread management +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +{ + switch(p) + { + // If the process is REALTIME_PRIORITY_CLASS then it could have priority values 3 through14 and -3 through -14. + case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; // 15 + case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; // 2 + case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; // 1 + case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; // 0 + case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; // -1 + case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; // -2 + case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; // -15 + } + return THREAD_PRIORITY_NORMAL; +} + +/* static */ +Thread::ThreadPriority Thread::GetOVRPriority(int osPriority) +{ + // If the process is REALTIME_PRIORITY_CLASS then it could have priority values 3 through14 and -3 through -14. + // As a result, it's possible for those cases that an unknown/invalid ThreadPriority enum be returned. However, + // in practice we don't expect to be using such processes. + + // The ThreadPriority types aren't linearly distributed, so we need to check for some values explicitly. + if(osPriority == THREAD_PRIORITY_TIME_CRITICAL) + return Thread::CriticalPriority; + if(osPriority == THREAD_PRIORITY_IDLE) + return Thread::IdlePriority; + return (ThreadPriority)(Thread::NormalPriority - osPriority); +} + +Thread::ThreadPriority Thread::GetPriority() +{ + int osPriority = ::GetThreadPriority(ThreadHandle); + + if(osPriority != THREAD_PRIORITY_ERROR_RETURN) + { + return GetOVRPriority(osPriority); + } + + return NormalPriority; +} + +/* static */ +Thread::ThreadPriority Thread::GetCurrentPriority() +{ + int osPriority = ::GetThreadPriority(::GetCurrentThread()); + + if(osPriority != THREAD_PRIORITY_ERROR_RETURN) + { + return GetOVRPriority(osPriority); + } + + return NormalPriority; +} + +bool Thread::SetPriority(ThreadPriority p) +{ + BOOL ret = ::SetThreadPriority(ThreadHandle, Thread::GetOSPriority(p)); + return (ret != FALSE); +} + +/* static */ +bool Thread::SetCurrentPriority(ThreadPriority p) +{ + BOOL ret = ::SetThreadPriority(::GetCurrentThread(), Thread::GetOSPriority(p)); + return (ret != FALSE); +} + + + +// The actual first function called on thread start +#if defined(OVR_OS_WIN32) +unsigned WINAPI Thread_Win32StartFn(void * phandle) +#else // Other Micorosft OSs... +DWORD WINAPI Thread_Win32StartFn(void *phandle) +#endif +{ + Thread * pthread = (Thread*)phandle; + if (pthread->Processor != -1) + { + DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); + } + BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set thread priority")); + OVR_UNUSED(ret); + + // Ensure that ThreadId is assigned once thread is running, in case + // beginthread hasn't filled it in yet. + pthread->IdValue = (ThreadId)::GetCurrentThreadId(); + + DWORD result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (unsigned) result; +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + // Free old thread handle before creating the new one + CleanupSystemThread(); + + // AddRef to us until the thread is finished. + AddRef(); + ThreadList::AddRunningThread(this); + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; +#if defined(OVR_OS_WIN32) + ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, + Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); +#else // Other Micorosft OSs... + DWORD TheThreadId; + ThreadHandle = CreateThread(0, (unsigned)StackSize, + Thread_Win32StartFn, this, 0, &TheThreadId); + IdValue = (ThreadId)TheThreadId; +#endif + + // Failed? Fail the function + if (ThreadHandle == 0) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) + { + SuspendCount++; + return 1; + } + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + // Decrement count, and resume thread if it is 0 + int32_t oldCount = SuspendCount.ExchangeAdd_Acquire(-1); + if (oldCount >= 1) + { + if (oldCount == 1) + { + if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) + { + return 1; + } + } + else + { + return 1; + } + } + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread. + // MA: Don't use TLS for now. + //if (GetThread() != this) + // return; + + // Call the virtual OnExit function. + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + // Call the exit function. +#if defined(OVR_OS_WIN32) // _endthreadex doesn't exist on other Microsoft OSs and instead we need to call ExitThread directly. + _endthreadex((unsigned)exitCode); +#else + ExitThread((unsigned)exitCode); +#endif +} + + +void Thread::CleanupSystemThread() +{ + if (ThreadHandle != 0) + { + ::CloseHandle(ThreadHandle); + ThreadHandle = 0; + } +} + +// *** Sleep functions +// static +bool Thread::Sleep(unsigned secs) +{ + ::Sleep(secs*1000); + return 1; +} + +// static +bool Thread::MSleep(unsigned msecs) +{ + ::Sleep(msecs); + return 1; +} + +// static +void Thread::YieldCurrentThread() +{ + YieldProcessor(); +} + +void Thread::SetThreadName( const char* name ) +{ + if(IdValue) + SetThreadName(name, IdValue); + // Else we don't know what thread to name. We can save the name and wait until the thread is created. +} + + +void Thread::SetThreadName(const char* name, ThreadId threadId) +{ + #if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) + // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + #pragma pack(push,8) + struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags; // Reserved for future use; must be zero + }; + #pragma pack(pop) + + THREADNAME_INFO info = { 0x1000, name, (DWORD)threadId, 0 }; + + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); + } + __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + { + return; + } + #endif // OVR_BUILD_SHIPPING +} + + +void Thread::SetCurrentThreadName( const char* name ) +{ + SetThreadName(name, (ThreadId)::GetCurrentThreadId()); +} + + +void Thread::GetThreadName(char* name, size_t /*nameCapacity*/, ThreadId /*threadId*/) +{ + // Not possible on Windows. + name[0] = 0; +} + + +void Thread::GetCurrentThreadName(char* name, size_t /*nameCapacity*/) +{ + // Not possible on Windows. + name[0] = 0; +} + + +// static +int Thread::GetCPUCount() +{ + SYSTEM_INFO sysInfo; + + #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) // GetNativeSystemInfo requires WinXP+ and a corresponding SDK (0x0501) or later. + GetNativeSystemInfo(&sysInfo); + #else + GetSystemInfo(&sysInfo); + #endif + + return (int) sysInfo.dwNumberOfProcessors; +} + +// Returns the unique Id of a thread it is called on, intended for +// comparison purposes. +ThreadId GetCurrentThreadId() +{ + return (ThreadId)::GetCurrentThreadId(); +} + +} // OVR + +#endif |