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