nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: None nuclear@0: Filename : OVR_Threads.h nuclear@0: Content : Contains thread-related (safe) functionality nuclear@0: Created : September 19, 2012 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: #ifndef OVR_Threads_h nuclear@0: #define OVR_Threads_h nuclear@0: nuclear@0: #include "OVR_Types.h" nuclear@0: #include "OVR_Atomic.h" nuclear@0: #include "OVR_RefCount.h" nuclear@0: #include "OVR_Array.h" nuclear@0: nuclear@0: // Defines the infinite wait delay timeout nuclear@0: #define OVR_WAIT_INFINITE 0xFFFFFFFF nuclear@0: nuclear@0: // To be defined in the project configuration options nuclear@0: #ifdef OVR_ENABLE_THREADS nuclear@0: nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ****** Declared classes nuclear@0: nuclear@0: // Declared with thread support only nuclear@0: class Mutex; nuclear@0: class WaitCondition; nuclear@0: class Event; nuclear@0: // Implementation forward declarations nuclear@0: class MutexImpl; nuclear@0: class WaitConditionImpl; nuclear@0: nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Mutex nuclear@0: nuclear@0: // Mutex class represents a system Mutex synchronization object that provides access nuclear@0: // serialization between different threads, allowing one thread mutually exclusive access nuclear@0: // to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. nuclear@0: nuclear@0: class Mutex nuclear@0: { nuclear@0: friend class WaitConditionImpl; nuclear@0: friend class MutexImpl; nuclear@0: nuclear@0: MutexImpl *pImpl; nuclear@0: nuclear@0: public: nuclear@0: // Constructor/destructor nuclear@0: Mutex(bool recursive = 1); nuclear@0: ~Mutex(); nuclear@0: nuclear@0: // Locking functions nuclear@0: void DoLock(); nuclear@0: bool TryLock(); nuclear@0: void Unlock(); nuclear@0: nuclear@0: // Returns 1 if the mutes is currently locked by another thread nuclear@0: // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. nuclear@0: bool IsLockedByAnotherThread(); nuclear@0: nuclear@0: // Locker class; Used for automatic locking of a mutex withing scope nuclear@0: class Locker nuclear@0: { nuclear@0: public: nuclear@0: Mutex *pMutex; nuclear@0: Locker(Mutex *pmutex) nuclear@0: { pMutex = pmutex; pMutex->DoLock(); } nuclear@0: ~Locker() nuclear@0: { pMutex->Unlock(); } nuclear@0: }; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** WaitCondition nuclear@0: nuclear@0: /* nuclear@0: WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. nuclear@0: Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that nuclear@0: call Notify() or NotifyAll(). nuclear@0: nuclear@0: The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then nuclear@0: starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same nuclear@0: resource, this ensures that any condition checked for while the mutex was locked does not change before nuclear@0: the wait on the condition is actually initiated. nuclear@0: */ nuclear@0: nuclear@0: class WaitCondition nuclear@0: { nuclear@0: friend class WaitConditionImpl; nuclear@0: // Internal implementation structure nuclear@0: WaitConditionImpl *pImpl; nuclear@0: nuclear@0: public: nuclear@0: // Constructor/destructor nuclear@0: WaitCondition(); nuclear@0: ~WaitCondition(); nuclear@0: nuclear@0: // Release mutex and wait for condition. The mutex is re-aquired after the wait. nuclear@0: // Delay is specified in milliseconds (1/1000 of a second). 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: //----------------------------------------------------------------------------------- nuclear@0: // ***** Event nuclear@0: nuclear@0: // Event is a wait-able synchronization object similar to Windows event. nuclear@0: // Event can be waited on until it's signaled by another thread calling nuclear@0: // either SetEvent or PulseEvent. nuclear@0: nuclear@0: class Event nuclear@0: { nuclear@0: // Event state, its mutex and the wait condition nuclear@0: volatile bool State; nuclear@0: volatile bool Temporary; nuclear@0: mutable Mutex StateMutex; nuclear@0: WaitCondition StateWaitCondition; nuclear@0: nuclear@0: void updateState(bool newState, bool newTemp, bool mustNotify); nuclear@0: nuclear@0: public: nuclear@0: Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } nuclear@0: ~Event() { } nuclear@0: nuclear@0: // Wait on an event condition until it is set nuclear@0: // Delay is specified in milliseconds (1/1000 of a second). nuclear@0: bool Wait(unsigned delay = OVR_WAIT_INFINITE); nuclear@0: nuclear@0: // Set an event, releasing objects waiting on it nuclear@0: void SetEvent() nuclear@0: { updateState(true, false, true); } nuclear@0: nuclear@0: // Reset an event, un-signaling it nuclear@0: void ResetEvent() nuclear@0: { updateState(false, false, false); } nuclear@0: nuclear@0: // Set and then reset an event once a waiter is released. nuclear@0: // If threads are already waiting, they will be notified and released nuclear@0: // If threads are not waiting, the event is set until the first thread comes in nuclear@0: void PulseEvent() nuclear@0: { updateState(true, true, true); } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Thread class nuclear@0: nuclear@0: // ThreadHandle is a handle to a thread, which on some platforms (e.g. Windows) is nuclear@0: // different from ThreadId. On Unix platforms, a ThreadHandle is the same as a nuclear@0: // ThreadId and is pthread_t. nuclear@0: typedef void* ThreadHandle; nuclear@0: nuclear@0: // ThreadId uniquely identifies a thread; returned by Windows GetCurrentThreadId(), nuclear@0: // Unix pthread_self() and Thread::GetThreadId. nuclear@0: typedef void* ThreadId; nuclear@0: nuclear@0: nuclear@0: // *** Thread flags nuclear@0: nuclear@0: // Indicates that the thread is has been started, i.e. Start method has been called, and threads nuclear@0: // OnExit() method has not yet been called/returned. nuclear@0: #define OVR_THREAD_STARTED 0x01 nuclear@0: // This flag is set once the thread has ran, and finished. nuclear@0: #define OVR_THREAD_FINISHED 0x02 nuclear@0: // This flag is set temporarily if this thread was started suspended. It is used internally. nuclear@0: #define OVR_THREAD_START_SUSPENDED 0x08 nuclear@0: // This flag is used to ask a thread to exit. Message driven threads will usually check this flag nuclear@0: // and finish once it is set. nuclear@0: #define OVR_THREAD_EXIT 0x10 nuclear@0: nuclear@0: nuclear@0: class Thread : public RefCountBase nuclear@0: { // NOTE: Waitable must be the first base since it implements RefCountImpl. nuclear@0: public: nuclear@0: // *** Callback functions, can be used instead of overriding Run nuclear@0: nuclear@0: // Run function prototypes. nuclear@0: // Thread function and user handle passed to it, executed by the default nuclear@0: // Thread::Run implementation if not null. nuclear@0: typedef int (*ThreadFn)(Thread *pthread, void* h); nuclear@0: nuclear@0: // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried nuclear@0: ThreadFn ThreadFunction; nuclear@0: // User handle passes to a thread nuclear@0: void* UserHandle; nuclear@0: nuclear@0: // Thread state to start a thread with nuclear@0: enum ThreadState nuclear@0: { nuclear@0: NotRunning = 0, nuclear@0: Running = 1, nuclear@0: Suspended = 2 nuclear@0: }; nuclear@0: nuclear@0: // Thread priority nuclear@0: enum ThreadPriority nuclear@0: { nuclear@0: CriticalPriority, nuclear@0: HighestPriority, nuclear@0: AboveNormalPriority, nuclear@0: NormalPriority, nuclear@0: BelowNormalPriority, nuclear@0: LowestPriority, nuclear@0: IdlePriority, nuclear@0: }; nuclear@0: nuclear@0: // Thread constructor parameters nuclear@0: struct CreateParams nuclear@0: { nuclear@0: CreateParams(ThreadFn func = 0, void* hand = 0, size_t ssize = 128 * 1024, nuclear@0: int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) nuclear@0: : threadFunction(func), userHandle(hand), stackSize(ssize), nuclear@0: processor(proc), initialState(state), priority(prior) {} nuclear@0: ThreadFn threadFunction; // Thread function nuclear@0: void* userHandle; // User handle passes to a thread nuclear@0: size_t stackSize; // Thread stack size nuclear@0: int processor; // Thread hardware processor nuclear@0: ThreadState initialState; // nuclear@0: ThreadPriority priority; // Thread priority nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // *** Constructors nuclear@0: nuclear@0: // A default constructor always creates a thread in NotRunning state, because nuclear@0: // the derived class has not yet been initialized. The derived class can call Start explicitly. nuclear@0: // "processor" parameter specifies which hardware processor this thread will be run on. nuclear@0: // -1 means OS decides this. Implemented only on Win32 nuclear@0: Thread(size_t stackSize = 128 * 1024, int processor = -1); nuclear@0: // Constructors that initialize the thread with a pointer to function. nuclear@0: // An option to start a thread is available, but it should not be used if classes are derived from Thread. nuclear@0: // "processor" parameter specifies which hardware processor this thread will be run on. nuclear@0: // -1 means OS decides this. Implemented only on Win32 nuclear@0: Thread(ThreadFn threadFunction, void* userHandle = 0, size_t stackSize = 128 * 1024, nuclear@0: int processor = -1, ThreadState initialState = NotRunning); nuclear@0: // Constructors that initialize the thread with a create parameters structure. nuclear@0: explicit Thread(const CreateParams& params); nuclear@0: nuclear@0: // Destructor. nuclear@0: virtual ~Thread(); nuclear@0: nuclear@0: // Waits for all Threads to finish; should be called only from the root nuclear@0: // application thread. Once this function returns, we know that all other nuclear@0: // thread's references to Thread object have been released. nuclear@0: static void OVR_CDECL FinishAllThreads(); nuclear@0: nuclear@0: nuclear@0: // *** Overridable Run function for thread processing nuclear@0: nuclear@0: // - returning from this method will end the execution of the thread nuclear@0: // - return value is usually 0 for success nuclear@0: virtual int Run(); nuclear@0: // Called after return/exit function nuclear@0: virtual void OnExit(); nuclear@0: nuclear@0: nuclear@0: // *** Thread management nuclear@0: nuclear@0: // Starts the thread if its not already running nuclear@0: // - internally sets up the threading and calls Run() nuclear@0: // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing nuclear@0: // - returns the exit code nuclear@0: virtual bool Start(ThreadState initialState = Running); nuclear@0: nuclear@0: // Quits with an exit code nuclear@0: virtual void Exit(int exitCode=0); nuclear@0: nuclear@0: // Suspend the thread until resumed nuclear@0: // Returns 1 for success, 0 for failure. nuclear@0: bool Suspend(); nuclear@0: // Resumes currently suspended thread nuclear@0: // Returns 1 for success, 0 for failure. nuclear@0: bool Resume(); nuclear@0: nuclear@0: // Static function to return a pointer to the current thread nuclear@0: //static Thread* GetThread(); nuclear@0: nuclear@0: nuclear@0: // *** Thread status query functions nuclear@0: nuclear@0: bool GetExitFlag() const; nuclear@0: void SetExitFlag(bool exitFlag); nuclear@0: nuclear@0: // Determines whether the thread was running and is now finished nuclear@0: bool IsFinished() const; nuclear@0: // Determines if the thread is currently suspended nuclear@0: bool IsSuspended() const; nuclear@0: // Returns current thread state nuclear@0: ThreadState GetThreadState() const; nuclear@0: nuclear@0: // Wait for thread to finish for a maxmimum number of milliseconds nuclear@0: // For maxWaitMs = 0 it simply polls and then returns if the thread is not finished nuclear@0: // For maxWaitMs < 0 it will wait forever nuclear@0: bool Join(int maxWaitMs = -1) const; nuclear@0: nuclear@0: // Returns the number of available CPUs on the system nuclear@0: static int GetCPUCount(); nuclear@0: nuclear@0: // Returns the thread exit code. Exit code is initialized to 0, nuclear@0: // and set to the return value if Run function after the thread is finished. nuclear@0: inline int GetExitCode() const { return ExitCode; } nuclear@0: // Returns an OS handle nuclear@0: #if defined(OVR_OS_MS) nuclear@0: void* GetOSHandle() const { return ThreadHandle; } nuclear@0: #else nuclear@0: pthread_t GetOSHandle() const { return ThreadHandle; } nuclear@0: #endif nuclear@0: nuclear@0: #if defined(OVR_OS_MS) nuclear@0: ThreadId GetThreadId() const { return IdValue; } nuclear@0: #else nuclear@0: ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } nuclear@0: #endif nuclear@0: nuclear@0: // Returns the platform-specific equivalent const that corresponds to the given ThreadPriority. nuclear@0: static int GetOSPriority(ThreadPriority); nuclear@0: static ThreadPriority GetOVRPriority(int osPriority); // May return a value outside the ThreadPriority enum range in unusual cases. nuclear@0: nuclear@0: // Gets this instance's priority. nuclear@0: ThreadPriority GetPriority(); nuclear@0: nuclear@0: // Gets the current thread's priority. nuclear@0: static ThreadPriority GetCurrentPriority(); nuclear@0: nuclear@0: // Sets this instance's thread's priority. nuclear@0: // Some platforms (e.g. Unix) don't let you set thread priorities unless you have root privileges/ nuclear@0: bool SetPriority(ThreadPriority); nuclear@0: nuclear@0: // Sets the current thread's priority. nuclear@0: static bool SetCurrentPriority(ThreadPriority); nuclear@0: nuclear@0: // *** Sleep nuclear@0: nuclear@0: // Sleep secs seconds nuclear@0: static bool Sleep(unsigned secs); nuclear@0: // Sleep msecs milliseconds nuclear@0: static bool MSleep(unsigned msecs); nuclear@0: nuclear@0: nuclear@0: // *** Debugging functionality nuclear@0: virtual void SetThreadName(const char* name); nuclear@0: static void SetThreadName(const char* name, ThreadId threadId); nuclear@0: static void SetCurrentThreadName(const char* name); nuclear@0: nuclear@0: static void GetThreadName(char* name, size_t nameCapacity, ThreadId threadId); nuclear@0: static void GetCurrentThreadName(char* name, size_t nameCapacity); nuclear@0: nuclear@0: private: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: friend unsigned WINAPI Thread_Win32StartFn(void *phandle); nuclear@0: #elif defined(OVR_OS_MS) // Any other Microsoft OS... nuclear@0: friend DWORD WINAPI Thread_Win32StartFn(void *phandle); nuclear@0: #else nuclear@0: friend void *Thread_PthreadStartFn(void * phandle); nuclear@0: nuclear@0: static int InitAttr; nuclear@0: static pthread_attr_t Attr; nuclear@0: #endif nuclear@0: nuclear@0: protected: nuclear@0: // Thread state flags nuclear@0: AtomicInt ThreadFlags; nuclear@0: AtomicInt SuspendCount; nuclear@0: size_t StackSize; nuclear@0: nuclear@0: // Hardware processor which this thread is running on. nuclear@0: int Processor; nuclear@0: ThreadPriority Priority; nuclear@0: nuclear@0: #if defined(OVR_OS_MS) nuclear@0: void* ThreadHandle; nuclear@0: volatile ThreadId IdValue; nuclear@0: nuclear@0: // System-specific cleanup function called from destructor nuclear@0: void CleanupSystemThread(); nuclear@0: nuclear@0: #else nuclear@0: pthread_t ThreadHandle; nuclear@0: #endif nuclear@0: nuclear@0: // Exit code of the thread, as returned by Run. nuclear@0: int ExitCode; nuclear@0: nuclear@0: // Internal run function. nuclear@0: int PRun(); nuclear@0: // Finishes the thread and releases internal reference to it. nuclear@0: void FinishAndRelease(); nuclear@0: nuclear@0: void Init(const CreateParams& params); nuclear@0: nuclear@0: // Protected copy constructor nuclear@0: Thread(const Thread &source) : RefCountBase() { OVR_UNUSED(source); } nuclear@0: nuclear@0: }; nuclear@0: nuclear@0: // Returns the unique Id of a thread it is called on, intended for nuclear@0: // comparison purposes. nuclear@0: ThreadId GetCurrentThreadId(); nuclear@0: nuclear@0: nuclear@0: } // OVR nuclear@0: nuclear@0: #endif // OVR_ENABLE_THREADS nuclear@0: #endif // OVR_Threads_h