nuclear@3: /************************************************************************************ nuclear@3: nuclear@3: Filename : OVR_ThreadsWinAPI.cpp nuclear@3: Platform : WinAPI nuclear@3: Content : Windows specific thread-related (safe) functionality nuclear@3: Created : September 19, 2012 nuclear@3: Notes : nuclear@3: nuclear@3: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@3: nuclear@3: Use of this software is subject to the terms of the Oculus license nuclear@3: agreement provided at the time of installation or download, or which nuclear@3: otherwise accompanies this software in either electronic or hard copy form. nuclear@3: nuclear@3: ************************************************************************************/ nuclear@3: nuclear@3: #include "OVR_Threads.h" nuclear@3: #include "OVR_Hash.h" nuclear@3: #include "OVR_Log.h" nuclear@3: nuclear@3: #ifdef OVR_ENABLE_THREADS nuclear@3: nuclear@3: // For _beginthreadex / _endtheadex nuclear@3: #include nuclear@3: nuclear@3: namespace OVR { nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // *** Internal Mutex implementation class nuclear@3: nuclear@3: class MutexImpl : public NewOverrideBase nuclear@3: { nuclear@3: // System mutex or semaphore nuclear@3: HANDLE hMutexOrSemaphore; nuclear@3: bool Recursive; nuclear@3: volatile unsigned LockCount; nuclear@3: nuclear@3: friend class WaitConditionImpl; nuclear@3: nuclear@3: public: nuclear@3: // Constructor/destructor nuclear@3: MutexImpl(bool recursive = 1); nuclear@3: ~MutexImpl(); nuclear@3: nuclear@3: // Locking functions nuclear@3: void DoLock(); nuclear@3: bool TryLock(); nuclear@3: void Unlock(Mutex* pmutex); nuclear@3: // Returns 1 if the mutes is currently locked nuclear@3: bool IsLockedByAnotherThread(Mutex* pmutex); nuclear@3: }; nuclear@3: nuclear@3: // *** Constructor/destructor nuclear@3: MutexImpl::MutexImpl(bool recursive) nuclear@3: { nuclear@3: Recursive = recursive; nuclear@3: LockCount = 0; nuclear@3: hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); nuclear@3: } nuclear@3: MutexImpl::~MutexImpl() nuclear@3: { nuclear@3: CloseHandle(hMutexOrSemaphore); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // Lock and try lock nuclear@3: void MutexImpl::DoLock() nuclear@3: { nuclear@3: if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) nuclear@3: return; nuclear@3: LockCount++; nuclear@3: } nuclear@3: nuclear@3: bool MutexImpl::TryLock() nuclear@3: { nuclear@3: DWORD ret; nuclear@3: if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) nuclear@3: return 0; nuclear@3: LockCount++; nuclear@3: return 1; nuclear@3: } nuclear@3: nuclear@3: void MutexImpl::Unlock(Mutex* pmutex) nuclear@3: { nuclear@3: OVR_UNUSED(pmutex); nuclear@3: nuclear@3: unsigned lockCount; nuclear@3: LockCount--; nuclear@3: lockCount = LockCount; nuclear@3: nuclear@3: // Release mutex nuclear@3: if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : nuclear@3: ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) nuclear@3: { nuclear@3: // This used to call Wait handlers if lockCount == 0. nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) nuclear@3: { nuclear@3: // There could be multiple interpretations of IsLocked with respect to current thread nuclear@3: if (LockCount == 0) nuclear@3: return 0; nuclear@3: if (!TryLock()) nuclear@3: return 1; nuclear@3: Unlock(pmutex); nuclear@3: return 0; nuclear@3: } nuclear@3: nuclear@3: /* nuclear@3: bool MutexImpl::IsSignaled() const nuclear@3: { nuclear@3: // An mutex is signaled if it is not locked ANYWHERE nuclear@3: // Note that this is different from IsLockedByAnotherThread function, nuclear@3: // that takes current thread into account nuclear@3: return LockCount == 0; nuclear@3: } nuclear@3: */ nuclear@3: nuclear@3: nuclear@3: // *** Actual Mutex class implementation nuclear@3: nuclear@3: Mutex::Mutex(bool recursive) nuclear@3: { nuclear@3: pImpl = new MutexImpl(recursive); nuclear@3: } nuclear@3: Mutex::~Mutex() nuclear@3: { nuclear@3: delete pImpl; nuclear@3: } nuclear@3: nuclear@3: // Lock and try lock nuclear@3: void Mutex::DoLock() nuclear@3: { nuclear@3: pImpl->DoLock(); nuclear@3: } nuclear@3: bool Mutex::TryLock() nuclear@3: { nuclear@3: return pImpl->TryLock(); nuclear@3: } nuclear@3: void Mutex::Unlock() nuclear@3: { nuclear@3: pImpl->Unlock(this); nuclear@3: } nuclear@3: bool Mutex::IsLockedByAnotherThread() nuclear@3: { nuclear@3: return pImpl->IsLockedByAnotherThread(this); nuclear@3: } nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Event nuclear@3: nuclear@3: bool Event::Wait(unsigned delay) nuclear@3: { nuclear@3: Mutex::Locker lock(&StateMutex); nuclear@3: nuclear@3: // Do the correct amount of waiting nuclear@3: if (delay == OVR_WAIT_INFINITE) nuclear@3: { nuclear@3: while(!State) nuclear@3: StateWaitCondition.Wait(&StateMutex); nuclear@3: } nuclear@3: else if (delay) nuclear@3: { nuclear@3: if (!State) nuclear@3: StateWaitCondition.Wait(&StateMutex, delay); nuclear@3: } nuclear@3: nuclear@3: bool state = State; nuclear@3: // Take care of temporary 'pulsing' of a state nuclear@3: if (Temporary) nuclear@3: { nuclear@3: Temporary = false; nuclear@3: State = false; nuclear@3: } nuclear@3: return state; nuclear@3: } nuclear@3: nuclear@3: void Event::updateState(bool newState, bool newTemp, bool mustNotify) nuclear@3: { nuclear@3: Mutex::Locker lock(&StateMutex); nuclear@3: State = newState; nuclear@3: Temporary = newTemp; nuclear@3: if (mustNotify) nuclear@3: StateWaitCondition.NotifyAll(); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Win32 Wait Condition Implementation nuclear@3: nuclear@3: // Internal implementation class nuclear@3: class WaitConditionImpl : public NewOverrideBase nuclear@3: { nuclear@3: // Event pool entries for extra events nuclear@3: struct EventPoolEntry : public NewOverrideBase nuclear@3: { nuclear@3: HANDLE hEvent; nuclear@3: EventPoolEntry *pNext; nuclear@3: EventPoolEntry *pPrev; nuclear@3: }; nuclear@3: nuclear@3: Lock WaitQueueLoc; nuclear@3: // Stores free events that can be used later nuclear@3: EventPoolEntry * pFreeEventList; nuclear@3: nuclear@3: // A queue of waiting objects to be signaled nuclear@3: EventPoolEntry* pQueueHead; nuclear@3: EventPoolEntry* pQueueTail; nuclear@3: nuclear@3: // Allocation functions for free events nuclear@3: EventPoolEntry* GetNewEvent(); nuclear@3: void ReleaseEvent(EventPoolEntry* pevent); nuclear@3: nuclear@3: // Queue operations nuclear@3: void QueuePush(EventPoolEntry* pentry); nuclear@3: EventPoolEntry* QueuePop(); nuclear@3: void QueueFindAndRemove(EventPoolEntry* pentry); nuclear@3: nuclear@3: public: nuclear@3: nuclear@3: // Constructor/destructor nuclear@3: WaitConditionImpl(); nuclear@3: ~WaitConditionImpl(); nuclear@3: nuclear@3: // Release mutex and wait for condition. The mutex is re-acqured after the wait. nuclear@3: bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); nuclear@3: nuclear@3: // Notify a condition, releasing at one object waiting nuclear@3: void Notify(); nuclear@3: // Notify a condition, releasing all objects waiting nuclear@3: void NotifyAll(); nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: nuclear@3: WaitConditionImpl::WaitConditionImpl() nuclear@3: { nuclear@3: pFreeEventList = 0; nuclear@3: pQueueHead = nuclear@3: pQueueTail = 0; nuclear@3: } nuclear@3: nuclear@3: WaitConditionImpl::~WaitConditionImpl() nuclear@3: { nuclear@3: // Free all the resources nuclear@3: EventPoolEntry* p = pFreeEventList; nuclear@3: EventPoolEntry* pentry; nuclear@3: nuclear@3: while(p) nuclear@3: { nuclear@3: // Move to next nuclear@3: pentry = p; nuclear@3: p = p->pNext; nuclear@3: // Delete old nuclear@3: ::CloseHandle(pentry->hEvent); nuclear@3: delete pentry; nuclear@3: } nuclear@3: // Shouldn't we also consider the queue? nuclear@3: nuclear@3: // To be safe nuclear@3: pFreeEventList = 0; nuclear@3: pQueueHead = nuclear@3: pQueueTail = 0; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // Allocation functions for free events nuclear@3: WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() nuclear@3: { nuclear@3: EventPoolEntry* pentry; nuclear@3: nuclear@3: // If there are any free nodes, use them nuclear@3: if (pFreeEventList) nuclear@3: { nuclear@3: pentry = pFreeEventList; nuclear@3: pFreeEventList = pFreeEventList->pNext; nuclear@3: } nuclear@3: else nuclear@3: { nuclear@3: // Allocate a new node nuclear@3: pentry = new EventPoolEntry; nuclear@3: pentry->pNext = 0; nuclear@3: pentry->pPrev = 0; nuclear@3: // Non-signaled manual event nuclear@3: pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); nuclear@3: } nuclear@3: nuclear@3: return pentry; nuclear@3: } nuclear@3: nuclear@3: void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) nuclear@3: { nuclear@3: // Mark event as non-signaled nuclear@3: ::ResetEvent(pevent->hEvent); nuclear@3: // And add it to free pool nuclear@3: pevent->pNext = pFreeEventList; nuclear@3: pevent->pPrev = 0; nuclear@3: pFreeEventList = pevent; nuclear@3: } nuclear@3: nuclear@3: // Queue operations nuclear@3: void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) nuclear@3: { nuclear@3: // Items already exist? Just add to tail nuclear@3: if (pQueueTail) nuclear@3: { nuclear@3: pentry->pPrev = pQueueTail; nuclear@3: pQueueTail->pNext = pentry; nuclear@3: pentry->pNext = 0; nuclear@3: pQueueTail = pentry; nuclear@3: } nuclear@3: else nuclear@3: { nuclear@3: // No items in queue nuclear@3: pentry->pNext = nuclear@3: pentry->pPrev = 0; nuclear@3: pQueueHead = nuclear@3: pQueueTail = pentry; nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() nuclear@3: { nuclear@3: EventPoolEntry* pentry = pQueueHead; nuclear@3: nuclear@3: // No items, null pointer nuclear@3: if (pentry) nuclear@3: { nuclear@3: // More items after this one? just grab the first item nuclear@3: if (pQueueHead->pNext) nuclear@3: { nuclear@3: pQueueHead = pentry->pNext; nuclear@3: pQueueHead->pPrev = 0; nuclear@3: } nuclear@3: else nuclear@3: { nuclear@3: // Last item left nuclear@3: pQueueTail = nuclear@3: pQueueHead = 0; nuclear@3: } nuclear@3: } nuclear@3: return pentry; nuclear@3: } nuclear@3: nuclear@3: void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) nuclear@3: { nuclear@3: // Do an exhaustive search looking for an entry nuclear@3: EventPoolEntry* p = pQueueHead; nuclear@3: nuclear@3: while(p) nuclear@3: { nuclear@3: // Entry found? Remove. nuclear@3: if (p == pentry) nuclear@3: { nuclear@3: nuclear@3: // Remove the node form the list nuclear@3: // Prev link nuclear@3: if (pentry->pPrev) nuclear@3: pentry->pPrev->pNext = pentry->pNext; nuclear@3: else nuclear@3: pQueueHead = pentry->pNext; nuclear@3: // Next link nuclear@3: if (pentry->pNext) nuclear@3: pentry->pNext->pPrev = pentry->pPrev; nuclear@3: else nuclear@3: pQueueTail = pentry->pPrev; nuclear@3: // Done nuclear@3: return; nuclear@3: } nuclear@3: nuclear@3: // Move to next item nuclear@3: p = p->pNext; nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: nuclear@3: bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) nuclear@3: { nuclear@3: bool result = 0; nuclear@3: unsigned i; nuclear@3: unsigned lockCount = pmutex->pImpl->LockCount; nuclear@3: EventPoolEntry* pentry; nuclear@3: nuclear@3: // Mutex must have been locked nuclear@3: if (lockCount == 0) nuclear@3: return 0; nuclear@3: nuclear@3: // Add an object to the wait queue nuclear@3: WaitQueueLoc.DoLock(); nuclear@3: QueuePush(pentry = GetNewEvent()); nuclear@3: WaitQueueLoc.Unlock(); nuclear@3: nuclear@3: // Finally, release a mutex or semaphore nuclear@3: if (pmutex->pImpl->Recursive) nuclear@3: { nuclear@3: // Release the recursive mutex N times nuclear@3: pmutex->pImpl->LockCount = 0; nuclear@3: for(i=0; ipImpl->hMutexOrSemaphore); nuclear@3: } nuclear@3: else nuclear@3: { nuclear@3: pmutex->pImpl->LockCount = 0; nuclear@3: ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); nuclear@3: } nuclear@3: nuclear@3: // Note that there is a gap here between mutex.Unlock() and Wait(). However, nuclear@3: // if notify() comes in at this point in the other thread it will set our nuclear@3: // corresponding event so wait will just fall through, as expected. nuclear@3: nuclear@3: // Block and wait on the event nuclear@3: DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, nuclear@3: (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); nuclear@3: /* nuclear@3: repeat_wait: nuclear@3: DWORD waitResult = nuclear@3: nuclear@3: ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, nuclear@3: (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, nuclear@3: QS_ALLINPUT); nuclear@3: */ nuclear@3: nuclear@3: WaitQueueLoc.DoLock(); nuclear@3: switch(waitResult) nuclear@3: { nuclear@3: case WAIT_ABANDONED: nuclear@3: case WAIT_OBJECT_0: nuclear@3: result = 1; nuclear@3: // Wait was successful, therefore the event entry should already be removed nuclear@3: // So just add entry back to a free list nuclear@3: ReleaseEvent(pentry); nuclear@3: break; nuclear@3: /* nuclear@3: case WAIT_OBJECT_0 + 1: nuclear@3: // Messages in WINDOWS queue nuclear@3: { nuclear@3: MSG msg; nuclear@3: PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); nuclear@3: WaitQueueLoc.Unlock(); nuclear@3: goto repeat_wait; nuclear@3: } nuclear@3: break; */ nuclear@3: default: nuclear@3: // Timeout, our entry should still be in a queue nuclear@3: QueueFindAndRemove(pentry); nuclear@3: ReleaseEvent(pentry); nuclear@3: } nuclear@3: WaitQueueLoc.Unlock(); nuclear@3: nuclear@3: // Re-aquire the mutex nuclear@3: for(i=0; iDoLock(); nuclear@3: nuclear@3: // Return the result nuclear@3: return result; nuclear@3: } nuclear@3: nuclear@3: // Notify a condition, releasing the least object in a queue nuclear@3: void WaitConditionImpl::Notify() nuclear@3: { nuclear@3: Lock::Locker lock(&WaitQueueLoc); nuclear@3: nuclear@3: // Pop last entry & signal it nuclear@3: EventPoolEntry* pentry = QueuePop(); nuclear@3: if (pentry) nuclear@3: ::SetEvent(pentry->hEvent); nuclear@3: } nuclear@3: nuclear@3: // Notify a condition, releasing all objects waiting nuclear@3: void WaitConditionImpl::NotifyAll() nuclear@3: { nuclear@3: Lock::Locker lock(&WaitQueueLoc); nuclear@3: nuclear@3: // Pop and signal all events nuclear@3: // NOTE : There is no need to release the events, it's the waiters job to do so nuclear@3: EventPoolEntry* pentry = QueuePop(); nuclear@3: while (pentry) nuclear@3: { nuclear@3: ::SetEvent(pentry->hEvent); nuclear@3: pentry = QueuePop(); nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: nuclear@3: nuclear@3: // *** Actual implementation of WaitCondition nuclear@3: nuclear@3: WaitCondition::WaitCondition() nuclear@3: { nuclear@3: pImpl = new WaitConditionImpl; nuclear@3: } nuclear@3: WaitCondition::~WaitCondition() nuclear@3: { nuclear@3: delete pImpl; nuclear@3: } nuclear@3: nuclear@3: // Wait without a mutex nuclear@3: bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) nuclear@3: { nuclear@3: return pImpl->Wait(pmutex, delay); nuclear@3: } nuclear@3: // Notification nuclear@3: void WaitCondition::Notify() nuclear@3: { nuclear@3: pImpl->Notify(); nuclear@3: } nuclear@3: void WaitCondition::NotifyAll() nuclear@3: { nuclear@3: pImpl->NotifyAll(); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Thread Class nuclear@3: nuclear@3: // Per-thread variable nuclear@3: // MA: Don't use TLS for now - portability issues with DLLs, etc. nuclear@3: /* nuclear@3: #if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) nuclear@3: __declspec(thread) Thread* pCurrentThread = 0; nuclear@3: #else nuclear@3: #pragma data_seg(".tls$") nuclear@3: __declspec(thread) Thread* pCurrentThread = 0; nuclear@3: #pragma data_seg(".rwdata") nuclear@3: #endif nuclear@3: */ nuclear@3: nuclear@3: // *** Thread constructors. nuclear@3: nuclear@3: Thread::Thread(UPInt stackSize, int processor) nuclear@3: { nuclear@3: CreateParams params; nuclear@3: params.stackSize = stackSize; nuclear@3: params.processor = processor; nuclear@3: Init(params); nuclear@3: } nuclear@3: nuclear@3: Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, nuclear@3: int processor, Thread::ThreadState initialState) nuclear@3: { nuclear@3: CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); nuclear@3: Init(params); nuclear@3: } nuclear@3: nuclear@3: Thread::Thread(const CreateParams& params) nuclear@3: { nuclear@3: Init(params); nuclear@3: } nuclear@3: void Thread::Init(const CreateParams& params) nuclear@3: { nuclear@3: // Clear the variables nuclear@3: ThreadFlags = 0; nuclear@3: ThreadHandle = 0; nuclear@3: IdValue = 0; nuclear@3: ExitCode = 0; nuclear@3: SuspendCount = 0; nuclear@3: StackSize = params.stackSize; nuclear@3: Processor = params.processor; nuclear@3: Priority = params.priority; nuclear@3: nuclear@3: // Clear Function pointers nuclear@3: ThreadFunction = params.threadFunction; nuclear@3: UserHandle = params.userHandle; nuclear@3: if (params.initialState != NotRunning) nuclear@3: Start(params.initialState); nuclear@3: nuclear@3: } nuclear@3: nuclear@3: Thread::~Thread() nuclear@3: { nuclear@3: // Thread should not running while object is being destroyed, nuclear@3: // this would indicate ref-counting issue. nuclear@3: //OVR_ASSERT(IsRunning() == 0); nuclear@3: nuclear@3: // Clean up thread. nuclear@3: CleanupSystemThread(); nuclear@3: ThreadHandle = 0; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // *** Overridable User functions. nuclear@3: nuclear@3: // Default Run implementation nuclear@3: int Thread::Run() nuclear@3: { nuclear@3: // Call pointer to function, if available. nuclear@3: return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; nuclear@3: } nuclear@3: void Thread::OnExit() nuclear@3: { nuclear@3: } nuclear@3: nuclear@3: // Finishes the thread and releases internal reference to it. nuclear@3: void Thread::FinishAndRelease() nuclear@3: { nuclear@3: // Note: thread must be US. nuclear@3: ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); nuclear@3: ThreadFlags |= OVR_THREAD_FINISHED; nuclear@3: nuclear@3: // Release our reference; this is equivalent to 'delete this' nuclear@3: // from the point of view of our thread. nuclear@3: Release(); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // *** ThreadList - used to tack all created threads nuclear@3: nuclear@3: class ThreadList : public NewOverrideBase nuclear@3: { nuclear@3: //------------------------------------------------------------------------ nuclear@3: struct ThreadHashOp nuclear@3: { nuclear@3: UPInt operator()(const Thread* ptr) nuclear@3: { nuclear@3: return (((UPInt)ptr) >> 6) ^ (UPInt)ptr; nuclear@3: } nuclear@3: }; nuclear@3: nuclear@3: HashSet ThreadSet; nuclear@3: Mutex ThreadMutex; nuclear@3: WaitCondition ThreadsEmpty; nuclear@3: // Track the root thread that created us. nuclear@3: ThreadId RootThreadId; nuclear@3: nuclear@3: static ThreadList* volatile pRunningThreads; nuclear@3: nuclear@3: void addThread(Thread *pthread) nuclear@3: { nuclear@3: Mutex::Locker lock(&ThreadMutex); nuclear@3: ThreadSet.Add(pthread); nuclear@3: } nuclear@3: nuclear@3: void removeThread(Thread *pthread) nuclear@3: { nuclear@3: Mutex::Locker lock(&ThreadMutex); nuclear@3: ThreadSet.Remove(pthread); nuclear@3: if (ThreadSet.GetSize() == 0) nuclear@3: ThreadsEmpty.Notify(); nuclear@3: } nuclear@3: nuclear@3: void finishAllThreads() nuclear@3: { nuclear@3: // Only original root thread can call this. nuclear@3: OVR_ASSERT(GetCurrentThreadId() == RootThreadId); nuclear@3: nuclear@3: Mutex::Locker lock(&ThreadMutex); nuclear@3: while (ThreadSet.GetSize() != 0) nuclear@3: ThreadsEmpty.Wait(&ThreadMutex); nuclear@3: } nuclear@3: nuclear@3: public: nuclear@3: nuclear@3: ThreadList() nuclear@3: { nuclear@3: RootThreadId = GetCurrentThreadId(); nuclear@3: } nuclear@3: ~ThreadList() { } nuclear@3: nuclear@3: nuclear@3: static void AddRunningThread(Thread *pthread) nuclear@3: { nuclear@3: // Non-atomic creation ok since only the root thread nuclear@3: if (!pRunningThreads) nuclear@3: { nuclear@3: pRunningThreads = new ThreadList; nuclear@3: OVR_ASSERT(pRunningThreads); nuclear@3: } nuclear@3: pRunningThreads->addThread(pthread); nuclear@3: } nuclear@3: nuclear@3: // NOTE: 'pthread' might be a dead pointer when this is nuclear@3: // called so it should not be accessed; it is only used nuclear@3: // for removal. nuclear@3: static void RemoveRunningThread(Thread *pthread) nuclear@3: { nuclear@3: OVR_ASSERT(pRunningThreads); nuclear@3: pRunningThreads->removeThread(pthread); nuclear@3: } nuclear@3: nuclear@3: static void FinishAllThreads() nuclear@3: { nuclear@3: // This is ok because only root thread can wait for other thread finish. nuclear@3: if (pRunningThreads) nuclear@3: { nuclear@3: pRunningThreads->finishAllThreads(); nuclear@3: delete pRunningThreads; nuclear@3: pRunningThreads = 0; nuclear@3: } nuclear@3: } nuclear@3: }; nuclear@3: nuclear@3: // By default, we have no thread list. nuclear@3: ThreadList* volatile ThreadList::pRunningThreads = 0; nuclear@3: nuclear@3: nuclear@3: // FinishAllThreads - exposed publicly in Thread. nuclear@3: void Thread::FinishAllThreads() nuclear@3: { nuclear@3: ThreadList::FinishAllThreads(); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // *** Run override nuclear@3: nuclear@3: int Thread::PRun() nuclear@3: { nuclear@3: // Suspend us on start, if requested nuclear@3: if (ThreadFlags & OVR_THREAD_START_SUSPENDED) nuclear@3: { nuclear@3: Suspend(); nuclear@3: ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; nuclear@3: } nuclear@3: nuclear@3: // Call the virtual run function nuclear@3: ExitCode = Run(); nuclear@3: return ExitCode; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: nuclear@3: /* MA: Don't use TLS for now. nuclear@3: nuclear@3: // Static function to return a pointer to the current thread nuclear@3: void Thread::InitCurrentThread(Thread *pthread) nuclear@3: { nuclear@3: pCurrentThread = pthread; nuclear@3: } nuclear@3: nuclear@3: // Static function to return a pointer to the current thread nuclear@3: Thread* Thread::GetThread() nuclear@3: { nuclear@3: return pCurrentThread; nuclear@3: } nuclear@3: */ nuclear@3: nuclear@3: nuclear@3: // *** User overridables nuclear@3: nuclear@3: bool Thread::GetExitFlag() const nuclear@3: { nuclear@3: return (ThreadFlags & OVR_THREAD_EXIT) != 0; nuclear@3: } nuclear@3: nuclear@3: void Thread::SetExitFlag(bool exitFlag) nuclear@3: { nuclear@3: // The below is atomic since ThreadFlags is AtomicInt. nuclear@3: if (exitFlag) nuclear@3: ThreadFlags |= OVR_THREAD_EXIT; nuclear@3: else nuclear@3: ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // Determines whether the thread was running and is now finished nuclear@3: bool Thread::IsFinished() const nuclear@3: { nuclear@3: return (ThreadFlags & OVR_THREAD_FINISHED) != 0; nuclear@3: } nuclear@3: // Determines whether the thread is suspended nuclear@3: bool Thread::IsSuspended() const nuclear@3: { nuclear@3: return SuspendCount > 0; nuclear@3: } nuclear@3: // Returns current thread state nuclear@3: Thread::ThreadState Thread::GetThreadState() const nuclear@3: { nuclear@3: if (IsSuspended()) nuclear@3: return Suspended; nuclear@3: if (ThreadFlags & OVR_THREAD_STARTED) nuclear@3: return Running; nuclear@3: return NotRunning; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: nuclear@3: // ***** Thread management nuclear@3: /* static */ nuclear@3: int Thread::GetOSPriority(ThreadPriority p) nuclear@3: { nuclear@3: switch(p) nuclear@3: { nuclear@3: case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; nuclear@3: case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; nuclear@3: case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; nuclear@3: case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; nuclear@3: case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; nuclear@3: case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; nuclear@3: case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; nuclear@3: } nuclear@3: return THREAD_PRIORITY_NORMAL; nuclear@3: } nuclear@3: nuclear@3: // The actual first function called on thread start nuclear@3: unsigned WINAPI Thread_Win32StartFn(void * phandle) nuclear@3: { nuclear@3: Thread * pthread = (Thread*)phandle; nuclear@3: if (pthread->Processor != -1) nuclear@3: { nuclear@3: DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); nuclear@3: if (ret == 0) nuclear@3: OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); nuclear@3: } nuclear@3: BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); nuclear@3: if (ret == 0) nuclear@3: OVR_DEBUG_LOG(("Could not set thread priority")); nuclear@3: OVR_UNUSED(ret); nuclear@3: nuclear@3: // Ensure that ThreadId is assigned once thread is running, in case nuclear@3: // beginthread hasn't filled it in yet. nuclear@3: pthread->IdValue = (ThreadId)::GetCurrentThreadId(); nuclear@3: nuclear@3: DWORD result = pthread->PRun(); nuclear@3: // Signal the thread as done and release it atomically. nuclear@3: pthread->FinishAndRelease(); nuclear@3: // At this point Thread object might be dead; however we can still pass nuclear@3: // it to RemoveRunningThread since it is only used as a key there. nuclear@3: ThreadList::RemoveRunningThread(pthread); nuclear@3: return (unsigned) result; nuclear@3: } nuclear@3: nuclear@3: bool Thread::Start(ThreadState initialState) nuclear@3: { nuclear@3: if (initialState == NotRunning) nuclear@3: return 0; nuclear@3: if (GetThreadState() != NotRunning) nuclear@3: { nuclear@3: OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); nuclear@3: return 0; nuclear@3: } nuclear@3: nuclear@3: // Free old thread handle before creating the new one nuclear@3: CleanupSystemThread(); nuclear@3: nuclear@3: // AddRef to us until the thread is finished. nuclear@3: AddRef(); nuclear@3: ThreadList::AddRunningThread(this); nuclear@3: nuclear@3: ExitCode = 0; nuclear@3: SuspendCount = 0; nuclear@3: ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; nuclear@3: ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, nuclear@3: Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); nuclear@3: nuclear@3: // Failed? Fail the function nuclear@3: if (ThreadHandle == 0) nuclear@3: { nuclear@3: ThreadFlags = 0; nuclear@3: Release(); nuclear@3: ThreadList::RemoveRunningThread(this); nuclear@3: return 0; nuclear@3: } nuclear@3: return 1; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // Suspend the thread until resumed nuclear@3: bool Thread::Suspend() nuclear@3: { nuclear@3: // Can't suspend a thread that wasn't started nuclear@3: if (!(ThreadFlags & OVR_THREAD_STARTED)) nuclear@3: return 0; nuclear@3: nuclear@3: if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) nuclear@3: { nuclear@3: SuspendCount++; nuclear@3: return 1; nuclear@3: } nuclear@3: return 0; nuclear@3: } nuclear@3: nuclear@3: // Resumes currently suspended thread nuclear@3: bool Thread::Resume() nuclear@3: { nuclear@3: // Can't suspend a thread that wasn't started nuclear@3: if (!(ThreadFlags & OVR_THREAD_STARTED)) nuclear@3: return 0; nuclear@3: nuclear@3: // Decrement count, and resume thread if it is 0 nuclear@3: SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1); nuclear@3: if (oldCount >= 1) nuclear@3: { nuclear@3: if (oldCount == 1) nuclear@3: { nuclear@3: if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) nuclear@3: return 1; nuclear@3: } nuclear@3: else nuclear@3: { nuclear@3: return 1; nuclear@3: } nuclear@3: } nuclear@3: return 0; nuclear@3: } nuclear@3: nuclear@3: nuclear@3: // Quits with an exit code nuclear@3: void Thread::Exit(int exitCode) nuclear@3: { nuclear@3: // Can only exist the current thread. nuclear@3: // MA: Don't use TLS for now. nuclear@3: //if (GetThread() != this) nuclear@3: // return; nuclear@3: nuclear@3: // Call the virtual OnExit function. nuclear@3: OnExit(); nuclear@3: nuclear@3: // Signal this thread object as done and release it's references. nuclear@3: FinishAndRelease(); nuclear@3: ThreadList::RemoveRunningThread(this); nuclear@3: nuclear@3: // Call the exit function. nuclear@3: _endthreadex((unsigned)exitCode); nuclear@3: } nuclear@3: nuclear@3: nuclear@3: void Thread::CleanupSystemThread() nuclear@3: { nuclear@3: if (ThreadHandle != 0) nuclear@3: { nuclear@3: ::CloseHandle(ThreadHandle); nuclear@3: ThreadHandle = 0; nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: // *** Sleep functions nuclear@3: // static nuclear@3: bool Thread::Sleep(unsigned secs) nuclear@3: { nuclear@3: ::Sleep(secs*1000); nuclear@3: return 1; nuclear@3: } nuclear@3: nuclear@3: // static nuclear@3: bool Thread::MSleep(unsigned msecs) nuclear@3: { nuclear@3: ::Sleep(msecs); nuclear@3: return 1; nuclear@3: } nuclear@3: nuclear@3: void Thread::SetThreadName( const char* name ) nuclear@3: { nuclear@3: #if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) nuclear@3: // Looks ugly, but it is the recommended way to name a thread. nuclear@3: typedef struct tagTHREADNAME_INFO { nuclear@3: DWORD dwType; // Must be 0x1000 nuclear@3: LPCSTR szName; // Pointer to name (in user address space) nuclear@3: DWORD dwThreadID; // Thread ID (-1 for caller thread) nuclear@3: DWORD dwFlags; // Reserved for future use; must be zero nuclear@3: } THREADNAME_INFO; nuclear@3: nuclear@3: THREADNAME_INFO info; nuclear@3: nuclear@3: info.dwType = 0x1000; nuclear@3: info.szName = name; nuclear@3: info.dwThreadID = reinterpret_cast(GetThreadId()); nuclear@3: info.dwFlags = 0; nuclear@3: nuclear@3: __try nuclear@3: { nuclear@3: #ifdef _WIN64 nuclear@3: RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info ); nuclear@3: #else nuclear@3: RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); nuclear@3: #endif nuclear@3: } nuclear@3: __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) nuclear@3: { nuclear@3: } nuclear@3: #endif // OVR_BUILD_SHIPPING nuclear@3: } nuclear@3: nuclear@3: // static nuclear@3: int Thread::GetCPUCount() nuclear@3: { nuclear@3: SYSTEM_INFO sysInfo; nuclear@3: GetSystemInfo(&sysInfo); nuclear@3: return (int) sysInfo.dwNumberOfProcessors; nuclear@3: } nuclear@3: nuclear@3: // Returns the unique Id of a thread it is called on, intended for nuclear@3: // comparison purposes. nuclear@3: ThreadId GetCurrentThreadId() nuclear@3: { nuclear@3: return (ThreadId)::GetCurrentThreadId(); nuclear@3: } nuclear@3: nuclear@3: } // OVR nuclear@3: nuclear@3: #endif nuclear@3: nuclear@3: