//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _PLATFORM_THREADS_THREAD_H_ #define _PLATFORM_THREADS_THREAD_H_ #ifndef _TORQUE_TYPES_H_ #include "platform/types.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif #ifndef _PLATFORM_THREADS_MUTEX_H_ #include "platform/threads/mutex.h" #endif #ifndef _TSINGLETON_H_ #include "core/util/tSingleton.h" #endif // Forward ref used by platform code class PlatformThreadData; // Typedefs typedef void (*ThreadRunFunction)(void *data); class Thread { public: typedef void Parent; protected: PlatformThreadData* mData; /// Used to signal threads need to stop. /// Threads set this flag to false in start() U32 shouldStop; /// Set the name of this thread for identification in debuggers. /// Maybe a NOP on platforms that do not support this. Always a NOP /// in non-debug builds. void _setName( const char* name ); public: /// If set, the thread will delete itself once it has finished running. bool autoDelete; /// Create a thread. /// @param func The starting function for the thread. /// @param arg Data to be passed to func, when the thread starts. /// @param start_thread Supported for compatibility. Must be false. Starting threads from /// within the constructor is not allowed anymore as the run() method is virtual. Thread(ThreadRunFunction func = 0, void *arg = 0, bool start_thread = false, bool autodelete = false); /// Destroy a thread. /// The thread MUST be allowed to exit before it is destroyed. virtual ~Thread(); /// Start a thread. /// Sets shouldStop to false and calls run() in a new thread of execution. void start( void* arg = 0 ); /// Ask a thread to stop running. void stop() { shouldStop = true; } /// Block until the thread stops running. /// @note Don't use this in combination with auto-deletion as otherwise the thread will kill /// itself while still executing the join() method on the waiting thread. bool join(); /// Threads may call checkForStop() periodically to check if they've been /// asked to stop. As soon as checkForStop() returns true, the thread should /// clean up and return. bool checkForStop() { return shouldStop; } /// Run the Thread's entry point function. /// Override this method in a subclass of Thread to create threaded code in /// an object oriented way, and without passing a function ptr to Thread(). /// Also, you can call this method directly to execute the thread's /// code in a non-threaded way. virtual void run(void *arg = 0); /// Returns true if the thread is running. bool isAlive(); /// Returns the platform specific thread id for this thread. dsize_t getId(); }; /// class ThreadManager { Vector threadPool; Mutex poolLock; struct MainThreadId { dsize_t mId; MainThreadId() { mId = ThreadManager::getCurrentThreadId(); } dsize_t get() { // Okay, this is a bit soso. The main thread ID may get queried during // global ctor phase before MainThreadId's ctor ran. Since global // ctors will/should all run on the main thread, we can sort of safely // assume here that we can just query the current thread's ID. if( !mId ) mId = ThreadManager::getCurrentThreadId(); return mId; } }; static MainThreadId smMainThreadId; public: ThreadManager() { VECTOR_SET_ASSOCIATION( threadPool ); } /// Return true if the caller is running on the main thread. static bool isMainThread(); /// Returns true if threadId is the same as the calling thread's id. static bool isCurrentThread(dsize_t threadId); /// Returns true if the 2 thread ids represent the same thread. Some thread /// APIs return an opaque object as a thread id, so the == operator cannot /// reliably compare thread ids. // this comparator is needed by pthreads and ThreadManager. static bool compare(dsize_t threadId_1, dsize_t threadId_2); /// Returns the platform specific thread id of the calling thread. Some /// platforms do not guarantee that this ID stays the same over the life of /// the thread, so use ThreadManager::compare() to compare thread ids. static dsize_t getCurrentThreadId(); /// Returns the platform specific thread id ot the main thread. static dsize_t getMainThreadId() { return smMainThreadId.get(); } /// Each thread should add itself to the thread pool the first time it runs. static void addThread(Thread* thread) { ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); manager.poolLock.lock(); Thread *alreadyAdded = getThreadById(thread->getId()); if(!alreadyAdded) manager.threadPool.push_back(thread); manager.poolLock.unlock(); } static void removeThread(Thread* thread) { ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); manager.poolLock.lock(); dsize_t threadID = thread->getId(); for(U32 i = 0;i < manager.threadPool.size();++i) { if( compare( manager.threadPool[i]->getId(), threadID ) ) { manager.threadPool.erase(i); break; } } manager.poolLock.unlock(); } /// Searches the pool of known threads for a thread whose id is equivalent to /// the given threadid. Compares thread ids with ThreadManager::compare(). static Thread* getThreadById(dsize_t threadid) { AssertFatal(threadid != 0, "ThreadManager::getThreadById() Searching for a bad thread id."); Thread* ret = NULL; ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); manager.poolLock.lock(); Vector &pool = manager.threadPool; for( S32 i = pool.size() - 1; i >= 0; i--) { Thread* p = pool[i]; if(compare(p->getId(), threadid)) { ret = p; break; } } manager.poolLock.unlock(); return ret; } static Thread* getCurrentThread() { return getThreadById(ThreadManager::getCurrentThreadId()); } static const char* getSingletonName() { return "ThreadManager"; } }; inline bool ThreadManager::isMainThread() { return compare( ThreadManager::getCurrentThreadId(), smMainThreadId.get() ); } inline bool ThreadManager::isCurrentThread(dsize_t threadId) { dsize_t current = getCurrentThreadId(); return compare(current, threadId); } #endif // _PLATFORM_THREADS_THREAD_H_