nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_ThreadsPthread.cpp nuclear@0: Content : nuclear@0: Created : nuclear@0: Notes : nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_Threads.h" nuclear@0: #include "OVR_Hash.h" nuclear@0: nuclear@0: #ifdef OVR_ENABLE_THREADS nuclear@0: nuclear@0: #include "OVR_Timer.h" nuclear@0: #include "OVR_Log.h" nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #if defined(OVR_OS_MAC) || defined(OVR_OS_BSD) nuclear@0: #include nuclear@0: #include nuclear@0: #if !defined(OVR_OS_MAC) nuclear@0: #include nuclear@0: #endif nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: // ***** Mutex implementation nuclear@0: nuclear@0: nuclear@0: // *** Internal Mutex implementation structure nuclear@0: nuclear@0: class MutexImpl : public NewOverrideBase nuclear@0: { nuclear@0: // System mutex or semaphore nuclear@0: pthread_mutex_t SMutex; nuclear@0: bool Recursive; nuclear@0: unsigned LockCount; nuclear@0: pthread_t LockedBy; nuclear@0: nuclear@0: friend class WaitConditionImpl; nuclear@0: nuclear@0: public: nuclear@0: // Constructor/destructor nuclear@0: MutexImpl(Mutex* pmutex, bool recursive = 1); nuclear@0: ~MutexImpl(); nuclear@0: nuclear@0: // Locking functions nuclear@0: void DoLock(); nuclear@0: bool TryLock(); nuclear@0: void Unlock(Mutex* pmutex); nuclear@0: // Returns 1 if the mutes is currently locked nuclear@0: bool IsLockedByAnotherThread(Mutex* pmutex); nuclear@0: bool IsSignaled() const; nuclear@0: }; nuclear@0: nuclear@0: pthread_mutexattr_t Lock::RecursiveAttr; nuclear@0: bool Lock::RecursiveAttrInit = 0; nuclear@0: nuclear@0: // *** Constructor/destructor nuclear@0: MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) nuclear@0: { nuclear@0: OVR_UNUSED(pmutex); nuclear@0: Recursive = recursive; nuclear@0: LockCount = 0; nuclear@0: nuclear@0: if (Recursive) nuclear@0: { nuclear@0: if (!Lock::RecursiveAttrInit) nuclear@0: { nuclear@0: pthread_mutexattr_init(&Lock::RecursiveAttr); nuclear@0: pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); nuclear@0: Lock::RecursiveAttrInit = 1; nuclear@0: } nuclear@0: nuclear@0: pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); nuclear@0: } nuclear@0: else nuclear@0: pthread_mutex_init(&SMutex, 0); nuclear@0: } nuclear@0: nuclear@0: MutexImpl::~MutexImpl() nuclear@0: { nuclear@0: pthread_mutex_destroy(&SMutex); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Lock and try lock nuclear@0: void MutexImpl::DoLock() nuclear@0: { nuclear@0: while (pthread_mutex_lock(&SMutex)) nuclear@0: ; nuclear@0: LockCount++; nuclear@0: LockedBy = pthread_self(); nuclear@0: } nuclear@0: nuclear@0: bool MutexImpl::TryLock() nuclear@0: { nuclear@0: if (!pthread_mutex_trylock(&SMutex)) nuclear@0: { nuclear@0: LockCount++; nuclear@0: LockedBy = pthread_self(); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void MutexImpl::Unlock(Mutex* pmutex) nuclear@0: { nuclear@0: OVR_UNUSED(pmutex); nuclear@0: OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); nuclear@0: nuclear@0: //unsigned lockCount; nuclear@0: LockCount--; nuclear@0: //lockCount = LockCount; nuclear@0: nuclear@0: pthread_mutex_unlock(&SMutex); nuclear@0: } nuclear@0: nuclear@0: bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) nuclear@0: { nuclear@0: OVR_UNUSED(pmutex); nuclear@0: // There could be multiple interpretations of IsLocked with respect to current thread nuclear@0: if (LockCount == 0) nuclear@0: return 0; nuclear@0: if (pthread_self() != LockedBy) nuclear@0: return 1; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: bool MutexImpl::IsSignaled() const nuclear@0: { nuclear@0: // An mutex is signaled if it is not locked ANYWHERE nuclear@0: // Note that this is different from IsLockedByAnotherThread function, nuclear@0: // that takes current thread into account nuclear@0: return LockCount == 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // *** Actual Mutex class implementation nuclear@0: nuclear@0: Mutex::Mutex(bool recursive) nuclear@0: { nuclear@0: // NOTE: RefCount mode already thread-safe for all waitables. nuclear@0: pImpl = new MutexImpl(this, recursive); nuclear@0: } nuclear@0: nuclear@0: Mutex::~Mutex() nuclear@0: { nuclear@0: delete pImpl; nuclear@0: } nuclear@0: nuclear@0: // Lock and try lock nuclear@0: void Mutex::DoLock() nuclear@0: { nuclear@0: pImpl->DoLock(); nuclear@0: } nuclear@0: bool Mutex::TryLock() nuclear@0: { nuclear@0: return pImpl->TryLock(); nuclear@0: } nuclear@0: void Mutex::Unlock() nuclear@0: { nuclear@0: pImpl->Unlock(this); nuclear@0: } nuclear@0: bool Mutex::IsLockedByAnotherThread() nuclear@0: { nuclear@0: return pImpl->IsLockedByAnotherThread(this); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Event nuclear@0: nuclear@0: bool Event::Wait(unsigned delay) nuclear@0: { nuclear@0: Mutex::Locker lock(&StateMutex); nuclear@0: nuclear@0: // Do the correct amount of waiting nuclear@0: if (delay == OVR_WAIT_INFINITE) nuclear@0: { nuclear@0: while(!State) nuclear@0: StateWaitCondition.Wait(&StateMutex); nuclear@0: } nuclear@0: else if (delay) nuclear@0: { nuclear@0: if (!State) nuclear@0: StateWaitCondition.Wait(&StateMutex, delay); nuclear@0: } nuclear@0: nuclear@0: bool state = State; nuclear@0: // Take care of temporary 'pulsing' of a state nuclear@0: if (Temporary) nuclear@0: { nuclear@0: Temporary = false; nuclear@0: State = false; nuclear@0: } nuclear@0: return state; nuclear@0: } nuclear@0: nuclear@0: void Event::updateState(bool newState, bool newTemp, bool mustNotify) nuclear@0: { nuclear@0: Mutex::Locker lock(&StateMutex); nuclear@0: State = newState; nuclear@0: Temporary = newTemp; nuclear@0: if (mustNotify) nuclear@0: StateWaitCondition.NotifyAll(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ***** Wait Condition Implementation nuclear@0: nuclear@0: // Internal implementation class nuclear@0: class WaitConditionImpl : public NewOverrideBase nuclear@0: { nuclear@0: pthread_mutex_t SMutex; nuclear@0: pthread_cond_t Condv; nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // Constructor/destructor nuclear@0: WaitConditionImpl(); nuclear@0: ~WaitConditionImpl(); nuclear@0: nuclear@0: // Release mutex and wait for condition. The mutex is re-aqured after the wait. nuclear@0: bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); nuclear@0: nuclear@0: // Notify a condition, releasing at one object waiting nuclear@0: void Notify(); nuclear@0: // Notify a condition, releasing all objects waiting nuclear@0: void NotifyAll(); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: WaitConditionImpl::WaitConditionImpl() nuclear@0: { nuclear@0: pthread_mutex_init(&SMutex, 0); nuclear@0: pthread_cond_init(&Condv, 0); nuclear@0: } nuclear@0: nuclear@0: WaitConditionImpl::~WaitConditionImpl() nuclear@0: { nuclear@0: pthread_mutex_destroy(&SMutex); nuclear@0: pthread_cond_destroy(&Condv); nuclear@0: } nuclear@0: nuclear@0: bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) nuclear@0: { nuclear@0: bool result = 1; nuclear@0: unsigned lockCount = pmutex->pImpl->LockCount; nuclear@0: nuclear@0: // Mutex must have been locked nuclear@0: if (lockCount == 0) nuclear@0: return 0; nuclear@0: nuclear@0: pthread_mutex_lock(&SMutex); nuclear@0: nuclear@0: // Finally, release a mutex or semaphore nuclear@0: if (pmutex->pImpl->Recursive) nuclear@0: { nuclear@0: // Release the recursive mutex N times nuclear@0: pmutex->pImpl->LockCount = 0; nuclear@0: for(unsigned i=0; ipImpl->SMutex); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: pmutex->pImpl->LockCount = 0; nuclear@0: pthread_mutex_unlock(&pmutex->pImpl->SMutex); nuclear@0: } nuclear@0: nuclear@0: // Note that there is a gap here between mutex.Unlock() and Wait(). nuclear@0: // The other mutex protects this gap. nuclear@0: nuclear@0: if (delay == OVR_WAIT_INFINITE) nuclear@0: pthread_cond_wait(&Condv,&SMutex); nuclear@0: else nuclear@0: { nuclear@0: timespec ts; nuclear@0: nuclear@0: struct timeval tv; nuclear@0: gettimeofday(&tv, 0); nuclear@0: nuclear@0: ts.tv_sec = tv.tv_sec + (delay / 1000); nuclear@0: ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; nuclear@0: nuclear@0: if (ts.tv_nsec > 999999999) nuclear@0: { nuclear@0: ts.tv_sec++; nuclear@0: ts.tv_nsec -= 1000000000; nuclear@0: } nuclear@0: int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); nuclear@0: OVR_ASSERT(r == 0 || r == ETIMEDOUT); nuclear@0: if (r) nuclear@0: result = 0; nuclear@0: } nuclear@0: nuclear@0: pthread_mutex_unlock(&SMutex); nuclear@0: nuclear@0: // Re-aquire the mutex nuclear@0: for(unsigned i=0; iDoLock(); nuclear@0: nuclear@0: // Return the result nuclear@0: return result; nuclear@0: } nuclear@0: nuclear@0: // Notify a condition, releasing the least object in a queue nuclear@0: void WaitConditionImpl::Notify() nuclear@0: { nuclear@0: pthread_mutex_lock(&SMutex); nuclear@0: pthread_cond_signal(&Condv); nuclear@0: pthread_mutex_unlock(&SMutex); nuclear@0: } nuclear@0: nuclear@0: // Notify a condition, releasing all objects waiting nuclear@0: void WaitConditionImpl::NotifyAll() nuclear@0: { nuclear@0: pthread_mutex_lock(&SMutex); nuclear@0: pthread_cond_broadcast(&Condv); nuclear@0: pthread_mutex_unlock(&SMutex); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // *** Actual implementation of WaitCondition nuclear@0: nuclear@0: WaitCondition::WaitCondition() nuclear@0: { nuclear@0: pImpl = new WaitConditionImpl; nuclear@0: } nuclear@0: WaitCondition::~WaitCondition() nuclear@0: { nuclear@0: delete pImpl; nuclear@0: } nuclear@0: nuclear@0: bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) nuclear@0: { nuclear@0: return pImpl->Wait(pmutex, delay); nuclear@0: } nuclear@0: // Notification nuclear@0: void WaitCondition::Notify() nuclear@0: { nuclear@0: pImpl->Notify(); nuclear@0: } nuclear@0: void WaitCondition::NotifyAll() nuclear@0: { nuclear@0: pImpl->NotifyAll(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ***** Current thread nuclear@0: nuclear@0: // Per-thread variable nuclear@0: /* nuclear@0: static __thread Thread* pCurrentThread = 0; nuclear@0: nuclear@0: // Static function to return a pointer to the current thread nuclear@0: void Thread::InitCurrentThread(Thread *pthread) nuclear@0: { nuclear@0: pCurrentThread = pthread; nuclear@0: } nuclear@0: nuclear@0: // Static function to return a pointer to the current thread nuclear@0: Thread* Thread::GetThread() nuclear@0: { nuclear@0: return pCurrentThread; nuclear@0: } nuclear@0: */ nuclear@0: nuclear@0: nuclear@0: // *** Thread constructors. nuclear@0: nuclear@0: Thread::Thread(UPInt stackSize, int processor) nuclear@0: { nuclear@0: // NOTE: RefCount mode already thread-safe for all Waitable objects. nuclear@0: CreateParams params; nuclear@0: params.stackSize = stackSize; nuclear@0: params.processor = processor; nuclear@0: Init(params); nuclear@0: } nuclear@0: nuclear@0: Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, nuclear@0: int processor, Thread::ThreadState initialState) nuclear@0: { nuclear@0: CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); nuclear@0: Init(params); nuclear@0: } nuclear@0: nuclear@0: Thread::Thread(const CreateParams& params) nuclear@0: { nuclear@0: Init(params); nuclear@0: } nuclear@0: nuclear@0: void Thread::Init(const CreateParams& params) nuclear@0: { nuclear@0: // Clear the variables nuclear@0: ThreadFlags = 0; nuclear@0: ThreadHandle = 0; nuclear@0: ExitCode = 0; nuclear@0: SuspendCount = 0; nuclear@0: StackSize = params.stackSize; nuclear@0: Processor = params.processor; nuclear@0: Priority = params.priority; nuclear@0: nuclear@0: // Clear Function pointers nuclear@0: ThreadFunction = params.threadFunction; nuclear@0: UserHandle = params.userHandle; nuclear@0: if (params.initialState != NotRunning) nuclear@0: Start(params.initialState); nuclear@0: } nuclear@0: nuclear@0: Thread::~Thread() nuclear@0: { nuclear@0: // Thread should not running while object is being destroyed, nuclear@0: // this would indicate ref-counting issue. nuclear@0: //OVR_ASSERT(IsRunning() == 0); nuclear@0: nuclear@0: // Clean up thread. nuclear@0: ThreadHandle = 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // *** Overridable User functions. nuclear@0: nuclear@0: // Default Run implementation nuclear@0: int Thread::Run() nuclear@0: { nuclear@0: // Call pointer to function, if available. nuclear@0: return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; nuclear@0: } nuclear@0: void Thread::OnExit() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Finishes the thread and releases internal reference to it. nuclear@0: void Thread::FinishAndRelease() nuclear@0: { nuclear@0: // Note: thread must be US. nuclear@0: ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); nuclear@0: ThreadFlags |= OVR_THREAD_FINISHED; nuclear@0: nuclear@0: // Release our reference; this is equivalent to 'delete this' nuclear@0: // from the point of view of our thread. nuclear@0: Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // *** ThreadList - used to track all created threads nuclear@0: nuclear@0: class ThreadList : public NewOverrideBase nuclear@0: { nuclear@0: //------------------------------------------------------------------------ nuclear@0: struct ThreadHashOp nuclear@0: { nuclear@0: size_t operator()(const Thread* ptr) nuclear@0: { nuclear@0: return (((size_t)ptr) >> 6) ^ (size_t)ptr; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: HashSet ThreadSet; nuclear@0: Mutex ThreadMutex; nuclear@0: WaitCondition ThreadsEmpty; nuclear@0: // Track the root thread that created us. nuclear@0: pthread_t RootThreadId; nuclear@0: nuclear@0: static ThreadList* volatile pRunningThreads; nuclear@0: nuclear@0: void addThread(Thread *pthread) nuclear@0: { nuclear@0: Mutex::Locker lock(&ThreadMutex); nuclear@0: ThreadSet.Add(pthread); nuclear@0: } nuclear@0: nuclear@0: void removeThread(Thread *pthread) nuclear@0: { nuclear@0: Mutex::Locker lock(&ThreadMutex); nuclear@0: ThreadSet.Remove(pthread); nuclear@0: if (ThreadSet.GetSize() == 0) nuclear@0: ThreadsEmpty.Notify(); nuclear@0: } nuclear@0: nuclear@0: void finishAllThreads() nuclear@0: { nuclear@0: // Only original root thread can call this. nuclear@0: OVR_ASSERT(pthread_self() == RootThreadId); nuclear@0: nuclear@0: Mutex::Locker lock(&ThreadMutex); nuclear@0: while (ThreadSet.GetSize() != 0) nuclear@0: ThreadsEmpty.Wait(&ThreadMutex); nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: ThreadList() nuclear@0: { nuclear@0: RootThreadId = pthread_self(); nuclear@0: } nuclear@0: ~ThreadList() { } nuclear@0: nuclear@0: nuclear@0: static void AddRunningThread(Thread *pthread) nuclear@0: { nuclear@0: // Non-atomic creation ok since only the root thread nuclear@0: if (!pRunningThreads) nuclear@0: { nuclear@0: pRunningThreads = new ThreadList; nuclear@0: OVR_ASSERT(pRunningThreads); nuclear@0: } nuclear@0: pRunningThreads->addThread(pthread); nuclear@0: } nuclear@0: nuclear@0: // NOTE: 'pthread' might be a dead pointer when this is nuclear@0: // called so it should not be accessed; it is only used nuclear@0: // for removal. nuclear@0: static void RemoveRunningThread(Thread *pthread) nuclear@0: { nuclear@0: OVR_ASSERT(pRunningThreads); nuclear@0: pRunningThreads->removeThread(pthread); nuclear@0: } nuclear@0: nuclear@0: static void FinishAllThreads() nuclear@0: { nuclear@0: // This is ok because only root thread can wait for other thread finish. nuclear@0: if (pRunningThreads) nuclear@0: { nuclear@0: pRunningThreads->finishAllThreads(); nuclear@0: delete pRunningThreads; nuclear@0: pRunningThreads = 0; nuclear@0: } nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: // By default, we have no thread list. nuclear@0: ThreadList* volatile ThreadList::pRunningThreads = 0; nuclear@0: nuclear@0: nuclear@0: // FinishAllThreads - exposed publicly in Thread. nuclear@0: void Thread::FinishAllThreads() nuclear@0: { nuclear@0: ThreadList::FinishAllThreads(); nuclear@0: } nuclear@0: nuclear@0: // *** Run override nuclear@0: nuclear@0: int Thread::PRun() nuclear@0: { nuclear@0: // Suspend us on start, if requested nuclear@0: if (ThreadFlags & OVR_THREAD_START_SUSPENDED) nuclear@0: { nuclear@0: Suspend(); nuclear@0: ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; nuclear@0: } nuclear@0: nuclear@0: // Call the virtual run function nuclear@0: ExitCode = Run(); nuclear@0: return ExitCode; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: nuclear@0: // *** User overridables nuclear@0: nuclear@0: bool Thread::GetExitFlag() const nuclear@0: { nuclear@0: return (ThreadFlags & OVR_THREAD_EXIT) != 0; nuclear@0: } nuclear@0: nuclear@0: void Thread::SetExitFlag(bool exitFlag) nuclear@0: { nuclear@0: // The below is atomic since ThreadFlags is AtomicInt. nuclear@0: if (exitFlag) nuclear@0: ThreadFlags |= OVR_THREAD_EXIT; nuclear@0: else nuclear@0: ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Determines whether the thread was running and is now finished nuclear@0: bool Thread::IsFinished() const nuclear@0: { nuclear@0: return (ThreadFlags & OVR_THREAD_FINISHED) != 0; nuclear@0: } nuclear@0: // Determines whether the thread is suspended nuclear@0: bool Thread::IsSuspended() const nuclear@0: { nuclear@0: return SuspendCount > 0; nuclear@0: } nuclear@0: // Returns current thread state nuclear@0: Thread::ThreadState Thread::GetThreadState() const nuclear@0: { nuclear@0: if (IsSuspended()) nuclear@0: return Suspended; nuclear@0: if (ThreadFlags & OVR_THREAD_STARTED) nuclear@0: return Running; nuclear@0: return NotRunning; nuclear@0: } nuclear@0: nuclear@0: // Join thread nuclear@0: bool Thread::Join(int maxWaitMs) const nuclear@0: { nuclear@0: // If polling, nuclear@0: if (maxWaitMs == 0) nuclear@0: { nuclear@0: // Just return if finished nuclear@0: return IsFinished(); nuclear@0: } nuclear@0: // If waiting forever, nuclear@0: else if (maxWaitMs > 0) nuclear@0: { nuclear@0: UInt32 t0 = Timer::GetTicksMs(); nuclear@0: nuclear@0: while (!IsFinished()) nuclear@0: { nuclear@0: UInt32 t1 = Timer::GetTicksMs(); nuclear@0: nuclear@0: // If the wait has expired, nuclear@0: int delta = (int)(t1 - t0); nuclear@0: if (delta >= maxWaitMs) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: Thread::MSleep(10); nuclear@0: } nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: while (!IsFinished()) nuclear@0: { nuclear@0: pthread_join(ThreadHandle, NULL); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: /* nuclear@0: static const char* mapsched_policy(int policy) nuclear@0: { nuclear@0: switch(policy) nuclear@0: { nuclear@0: case SCHED_OTHER: nuclear@0: return "SCHED_OTHER"; nuclear@0: case SCHED_RR: nuclear@0: return "SCHED_RR"; nuclear@0: case SCHED_FIFO: nuclear@0: return "SCHED_FIFO"; nuclear@0: nuclear@0: } nuclear@0: return "UNKNOWN"; nuclear@0: } nuclear@0: int policy; nuclear@0: sched_param sparam; nuclear@0: pthread_getschedparam(pthread_self(), &policy, &sparam); nuclear@0: int max_prior = sched_get_priority_max(policy); nuclear@0: int min_prior = sched_get_priority_min(policy); nuclear@0: printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); nuclear@0: #include nuclear@0: */ nuclear@0: // ***** Thread management nuclear@0: nuclear@0: // The actual first function called on thread start nuclear@0: void* Thread_PthreadStartFn(void* phandle) nuclear@0: { nuclear@0: Thread* pthread = (Thread*)phandle; nuclear@0: int result = pthread->PRun(); nuclear@0: // Signal the thread as done and release it atomically. nuclear@0: pthread->FinishAndRelease(); nuclear@0: // At this point Thread object might be dead; however we can still pass nuclear@0: // it to RemoveRunningThread since it is only used as a key there. nuclear@0: ThreadList::RemoveRunningThread(pthread); nuclear@0: return reinterpret_cast(result); nuclear@0: } nuclear@0: nuclear@0: int Thread::InitAttr = 0; nuclear@0: pthread_attr_t Thread::Attr; nuclear@0: nuclear@0: /* static */ nuclear@0: int Thread::GetOSPriority(ThreadPriority p) nuclear@0: { nuclear@0: OVR_UNUSED(p); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: /* static */ nuclear@0: Thread::ThreadPriority Thread::GetOVRPriority(int osPriority) nuclear@0: { nuclear@0: #if defined(OVR_OS_LINUX) nuclear@0: return (ThreadPriority)(Thread::NormalPriority - osPriority); // This works for both SCHED_OTHER, SCHED_RR, and SCHED_FIFO. nuclear@0: #else nuclear@0: // Apple priorities are such that the min is a value less than the max. nuclear@0: static int minPriority = sched_get_priority_min(SCHED_FIFO); // We don't have a means to pass a policy type to this function. nuclear@0: static int maxPriority = sched_get_priority_max(SCHED_FIFO); nuclear@0: nuclear@0: return (ThreadPriority)(Thread::NormalPriority - (osPriority - ((minPriority + maxPriority) / 2))); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: Thread::ThreadPriority Thread::GetPriority() nuclear@0: { nuclear@0: int policy; nuclear@0: sched_param param; nuclear@0: nuclear@0: int result = pthread_getschedparam(ThreadHandle, &policy, ¶m); nuclear@0: nuclear@0: if(result == 0) nuclear@0: { nuclear@0: #if !defined(OVR_OS_LINUX) nuclear@0: if(policy == SCHED_OTHER) nuclear@0: { nuclear@0: return Thread::NormalPriority; //SCHED_OTHER allows only normal priority on BSD-style Unix and Mac OS X. nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: return GetOVRPriority(param.sched_priority); nuclear@0: } nuclear@0: nuclear@0: return Thread::NormalPriority; nuclear@0: } nuclear@0: nuclear@0: /* static */ nuclear@0: Thread::ThreadPriority Thread::GetCurrentPriority() nuclear@0: { nuclear@0: int policy; nuclear@0: sched_param param; nuclear@0: pthread_t currentThreadId = pthread_self(); nuclear@0: nuclear@0: int result = pthread_getschedparam(currentThreadId, &policy, ¶m); nuclear@0: nuclear@0: if(result == 0) nuclear@0: { nuclear@0: #if !defined(OVR_OS_LINUX) nuclear@0: if(policy == SCHED_OTHER) nuclear@0: { nuclear@0: return Thread::NormalPriority; //SCHED_OTHER allows only normal priority on BSD-style Unix and Mac OS X. nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: return GetOVRPriority(param.sched_priority); nuclear@0: } nuclear@0: nuclear@0: return Thread::NormalPriority; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: bool Thread::SetPriority(ThreadPriority) nuclear@0: { nuclear@0: // We currently fail. To do: add code to support this via pthread_getschedparam/pthread_attr_setschedparam nuclear@0: // This won't work unless using SCHED_FIFO or SCHED_RR anyway, which require root privileges. nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: /* static */ nuclear@0: bool Thread::SetCurrentPriority(ThreadPriority) nuclear@0: { nuclear@0: // We currently fail. To do: add code to support this via pthread_getschedparam/pthread_attr_setschedparam nuclear@0: // This won't work unless using SCHED_FIFO or SCHED_RR anyway, which require root privileges. nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: bool Thread::Start(ThreadState initialState) nuclear@0: { nuclear@0: if (initialState == NotRunning) nuclear@0: return 0; nuclear@0: if (GetThreadState() != NotRunning) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if (!InitAttr) nuclear@0: { nuclear@0: pthread_attr_init(&Attr); nuclear@0: pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); nuclear@0: pthread_attr_setstacksize(&Attr, 128 * 1024); nuclear@0: sched_param sparam; nuclear@0: sparam.sched_priority = Thread::GetOSPriority(NormalPriority); nuclear@0: pthread_attr_setschedparam(&Attr, &sparam); nuclear@0: InitAttr = 1; nuclear@0: } nuclear@0: nuclear@0: ExitCode = 0; nuclear@0: SuspendCount = 0; nuclear@0: ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; nuclear@0: nuclear@0: // AddRef to us until the thread is finished nuclear@0: AddRef(); nuclear@0: ThreadList::AddRunningThread(this); nuclear@0: nuclear@0: int result; nuclear@0: if (StackSize != 128 * 1024 || Priority != NormalPriority) nuclear@0: { nuclear@0: pthread_attr_t attr; nuclear@0: nuclear@0: pthread_attr_init(&attr); nuclear@0: pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); nuclear@0: pthread_attr_setstacksize(&attr, StackSize); nuclear@0: sched_param sparam; nuclear@0: sparam.sched_priority = Thread::GetOSPriority(Priority); nuclear@0: pthread_attr_setschedparam(&attr, &sparam); nuclear@0: result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); nuclear@0: pthread_attr_destroy(&attr); nuclear@0: } nuclear@0: else nuclear@0: result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); nuclear@0: nuclear@0: if (result) nuclear@0: { nuclear@0: ThreadFlags = 0; nuclear@0: Release(); nuclear@0: ThreadList::RemoveRunningThread(this); nuclear@0: return 0; nuclear@0: } nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Suspend the thread until resumed nuclear@0: bool Thread::Suspend() nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: // Resumes currently suspended thread nuclear@0: bool Thread::Resume() nuclear@0: { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Quits with an exit code nuclear@0: void Thread::Exit(int exitCode) nuclear@0: { nuclear@0: // Can only exist the current thread nuclear@0: // if (GetThread() != this) nuclear@0: // return; nuclear@0: nuclear@0: // Call the virtual OnExit function nuclear@0: OnExit(); nuclear@0: nuclear@0: // Signal this thread object as done and release it's references. nuclear@0: FinishAndRelease(); nuclear@0: ThreadList::RemoveRunningThread(this); nuclear@0: nuclear@0: pthread_exit(reinterpret_cast(exitCode)); nuclear@0: } nuclear@0: nuclear@0: ThreadId GetCurrentThreadId() nuclear@0: { nuclear@0: return (void*)pthread_self(); nuclear@0: } nuclear@0: nuclear@0: // *** Sleep functions nuclear@0: nuclear@0: /* static */ nuclear@0: bool Thread::Sleep(unsigned secs) nuclear@0: { nuclear@0: sleep(secs); nuclear@0: return 1; nuclear@0: } nuclear@0: /* static */ nuclear@0: bool Thread::MSleep(unsigned msecs) nuclear@0: { nuclear@0: usleep(msecs*1000); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: /* static */ nuclear@0: int Thread::GetCPUCount() nuclear@0: { nuclear@0: #if defined(OVR_OS_MAC) || defined(OVR_OS_BSD) nuclear@0: // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html nuclear@0: int cpuCount = 0; nuclear@0: size_t len = sizeof(cpuCount); nuclear@0: nuclear@0: if(sysctlbyname("hw.logicalcpu", &cpuCount, &len, NULL, 0) != 0) nuclear@0: cpuCount = 1; nuclear@0: nuclear@0: return cpuCount; nuclear@0: nuclear@0: #else // Linux, Android nuclear@0: nuclear@0: // Alternative: read /proc/cpuinfo nuclear@0: #ifdef _SC_NPROCESSORS_ONLN nuclear@0: return (int)sysconf(_SC_NPROCESSORS_ONLN); nuclear@0: #else nuclear@0: return 1; nuclear@0: #endif nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void Thread::SetThreadName( const char* name ) nuclear@0: { nuclear@0: #if defined (OVR_OS_APPLE) nuclear@0: if(ThreadHandle == pthread_self()) nuclear@0: pthread_setname_np(name); nuclear@0: // Else there's nothing we can do. nuclear@0: #else nuclear@0: if(ThreadHandle != 0) nuclear@0: pthread_setname_np(ThreadHandle, name); nuclear@0: // Else we can possibly save this name and set it later when the thread starts. nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void Thread::SetThreadName(const char* name, ThreadId threadId) nuclear@0: { nuclear@0: #if defined (OVR_OS_APPLE) nuclear@0: if(pthread_equal((pthread_t)threadId, pthread_self())) nuclear@0: pthread_setname_np(name); nuclear@0: // Else there's no way to set the name of another thread. nuclear@0: #else nuclear@0: pthread_setname_np((pthread_t)threadId, name); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void Thread::SetCurrentThreadName(const char* name) nuclear@0: { nuclear@0: #if defined (OVR_OS_APPLE) nuclear@0: pthread_setname_np(name); nuclear@0: #else nuclear@0: pthread_setname_np(pthread_self(), name); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void Thread::GetThreadName(char* name, size_t nameCapacity, ThreadId threadId) nuclear@0: { nuclear@0: name[0] = 0; nuclear@0: pthread_getname_np((pthread_t)threadId, name, nameCapacity); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void Thread::GetCurrentThreadName(char* name, size_t nameCapacity) nuclear@0: { nuclear@0: name[0] = 0; nuclear@0: pthread_getname_np(pthread_self(), name, nameCapacity); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } // namespace OVR nuclear@0: nuclear@0: #endif // OVR_ENABLE_THREADS