123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #ifndef __THREAD_H__
- #define __THREAD_H__
- /*
- ================================================
- idSysMutex provides a C++ wrapper to the low level system mutex functions. A mutex is an
- object that can only be locked by one thread at a time. It's used to prevent two threads
- from accessing the same piece of data simultaneously.
- ================================================
- */
- class idSysMutex {
- public:
- idSysMutex() { Sys_MutexCreate( handle ); }
- ~idSysMutex() { Sys_MutexDestroy( handle ); }
- bool Lock( bool blocking = true ) { return Sys_MutexLock( handle, blocking ); }
- void Unlock() { Sys_MutexUnlock( handle ); }
- private:
- mutexHandle_t handle;
- idSysMutex( const idSysMutex & s ) {}
- void operator=( const idSysMutex & s ) {}
- };
- /*
- ================================================
- idScopedCriticalSection is a helper class that automagically locks a mutex when it's created
- and unlocks it when it goes out of scope.
- ================================================
- */
- class idScopedCriticalSection {
- public:
- idScopedCriticalSection( idSysMutex & m ) : mutex(&m) { mutex->Lock(); }
- ~idScopedCriticalSection() { mutex->Unlock(); }
- private:
- idSysMutex * mutex; // NOTE: making this a reference causes a TypeInfo crash
- };
- /*
- ================================================
- idSysSignal is a C++ wrapper for the low level system signal functions. A signal is an object
- that a thread can wait on for it to be raised. It's used to indicate data is available or that
- a thread has reached a specific point.
- ================================================
- */
- class idSysSignal {
- public:
- static const int WAIT_INFINITE = -1;
- idSysSignal( bool manualReset = false ) { Sys_SignalCreate( handle, manualReset ); }
- ~idSysSignal() { Sys_SignalDestroy( handle ); }
- void Raise() { Sys_SignalRaise( handle ); }
- void Clear() { Sys_SignalClear( handle ); }
- // Wait returns true if the object is in a signalled state and
- // returns false if the wait timed out. Wait also clears the signalled
- // state when the signalled state is reached within the time out period.
- bool Wait( int timeout = WAIT_INFINITE ) { return Sys_SignalWait( handle, timeout ); }
- private:
- signalHandle_t handle;
- idSysSignal( const idSysSignal & s ) {}
- void operator=( const idSysSignal & s ) {}
- };
- /*
- ================================================
- idSysInterlockedInteger is a C++ wrapper for the low level system interlocked integer
- routines to atomically increment or decrement an integer.
- ================================================
- */
- class idSysInterlockedInteger {
- public:
- idSysInterlockedInteger() : value( 0 ) {}
- // atomically increments the integer and returns the new value
- int Increment() { return Sys_InterlockedIncrement( value ); }
- // atomically decrements the integer and returns the new value
- int Decrement() { return Sys_InterlockedDecrement( value ); }
- // atomically adds a value to the integer and returns the new value
- int Add( int v ) { return Sys_InterlockedAdd( value, (interlockedInt_t) v ); }
- // atomically subtracts a value from the integer and returns the new value
- int Sub( int v ) { return Sys_InterlockedSub( value, (interlockedInt_t) v ); }
- // returns the current value of the integer
- int GetValue() const { return value; }
- // sets a new value, Note: this operation is not atomic
- void SetValue( int v ) { value = (interlockedInt_t)v; }
- private:
- interlockedInt_t value;
- };
- /*
- ================================================
- idSysInterlockedPointer is a C++ wrapper around the low level system interlocked pointer
- routine to atomically set a pointer while retrieving the previous value of the pointer.
- ================================================
- */
- template< typename T >
- class idSysInterlockedPointer {
- public:
- idSysInterlockedPointer() : ptr( NULL ) {}
- // atomically sets the pointer and returns the previous pointer value
- T * Set( T * newPtr ) {
- return (T *) Sys_InterlockedExchangePointer( (void * &) ptr, newPtr );
- }
- // atomically sets the pointer to 'newPtr' only if the previous pointer is equal to 'comparePtr'
- // ptr = ( ptr == comparePtr ) ? newPtr : ptr
- T * CompareExchange( T * comparePtr, T * newPtr ) {
- return (T *) Sys_InterlockedCompareExchangePointer( (void * &) ptr, comparePtr, newPtr );
- }
- // returns the current value of the pointer
- T * Get() const { return ptr; }
- private:
- T * ptr;
- };
- /*
- ================================================
- idSysThread is an abstract base class, to be extended by classes implementing the
- idSysThread::Run() method.
- class idMyThread : public idSysThread {
- public:
- virtual int Run() {
- // run thread code here
- return 0;
- }
- // specify thread data here
- };
- idMyThread thread;
- thread.Start( "myThread" );
- A worker thread is a thread that waits in place (without consuming CPU)
- until work is available. A worker thread is implemented as normal, except that, instead of
- calling the Start() method, the StartWorker() method is called to start the thread.
- Note that the Sys_CreateThread function does not support the concept of worker threads.
- class idMyWorkerThread : public idSysThread {
- public:
- virtual int Run() {
- // run thread code here
- return 0;
- }
- // specify thread data here
- };
- idMyWorkerThread thread;
- thread.StartThread( "myWorkerThread" );
-
- // main thread loop
- for ( ; ; ) {
- // setup work for the thread here (by modifying class data on the thread)
- thread.SignalWork(); // kick in the worker thread
- // run other code in the main thread here (in parallel with the worker thread)
- thread.WaitForThread(); // wait for the worker thread to finish
- // use results from worker thread here
- }
- In the above example, the thread does not continuously run in parallel with the main Thread,
- but only for a certain period of time in a very controlled manner. Work is set up for the
- Thread and then the thread is signalled to process that work while the main thread continues.
- After doing other work, the main thread can wait for the worker thread to finish, if it has not
- finished already. When the worker thread is done, the main thread can safely use the results
- from the worker thread.
- Note that worker threads are useful on all platforms but they do not map to the SPUs on the PS3.
- ================================================
- */
- class idSysThread {
- public:
- idSysThread();
- virtual ~idSysThread();
- const char * GetName() const { return name.c_str(); }
- uintptr_t GetThreadHandle() const { return threadHandle; }
- bool IsRunning() const { return isRunning; }
- bool IsTerminating() const { return isTerminating; }
- //------------------------
- // Thread Start/Stop/Wait
- //------------------------
- bool StartThread( const char * name, core_t core,
- xthreadPriority priority = THREAD_NORMAL,
- int stackSize = DEFAULT_THREAD_STACK_SIZE );
- bool StartWorkerThread( const char * name, core_t core,
- xthreadPriority priority = THREAD_NORMAL,
- int stackSize = DEFAULT_THREAD_STACK_SIZE );
- void StopThread( bool wait = true );
- // This can be called from multiple other threads. However, in the case
- // of a worker thread, the work being "done" has little meaning if other
- // threads are continuously signalling more work.
- void WaitForThread();
- //------------------------
- // Worker Thread
- //------------------------
- // Signals the thread to notify work is available.
- // This can be called from multiple other threads.
- void SignalWork();
- // Returns true if the work is done without waiting.
- // This can be called from multiple other threads. However, the work
- // being "done" has little meaning if other threads are continuously
- // signalling more work.
- bool IsWorkDone();
- protected:
- // The routine that performs the work.
- virtual int Run();
- private:
- idStr name;
- uintptr_t threadHandle;
- bool isWorker;
- bool isRunning;
- volatile bool isTerminating;
- volatile bool moreWorkToDo;
- idSysSignal signalWorkerDone;
- idSysSignal signalMoreWorkToDo;
- idSysMutex signalMutex;
- static int ThreadProc( idSysThread * thread );
- idSysThread( const idSysThread & s ) {}
- void operator=( const idSysThread & s ) {}
- };
- /*
- ================================================
- idSysWorkerThreadGroup implements a group of worker threads that
- typically crunch through a collection of similar tasks.
- class idMyWorkerThread : public idSysThread {
- public:
- virtual int Run() {
- // run thread code here
- return 0;
- }
- // specify thread data here
- };
- idSysWorkerThreadGroup<idMyWorkerThread> workers( "myWorkers", 4 );
- for ( ; ; ) {
- for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
- // workers.GetThread( i )-> // setup work for this thread
- }
- workers.SignalWorkAndWait();
- // use results from the worker threads here
- }
- The concept of worker thread Groups is probably most useful for tools and compilers.
- For instance, the AAS Compiler is using a worker thread group. Although worker threads
- will work well on the PC, Mac and the 360, they do not directly map to the PS3,
- in that the worker threads won't automatically run on the SPUs.
- ================================================
- */
- template<class threadType>
- class idSysWorkerThreadGroup {
- public:
- idSysWorkerThreadGroup( const char * name, int numThreads,
- xthreadPriority priority = THREAD_NORMAL,
- int stackSize = DEFAULT_THREAD_STACK_SIZE );
- virtual ~idSysWorkerThreadGroup();
- int GetNumThreads() const { return threadList.Num(); }
- threadType & GetThread( int i ) { return *threadList[i]; }
- void SignalWorkAndWait();
- private:
- idList<threadType *, TAG_THREAD> threadList;
- bool runOneThreadInline; // use the signalling thread as one of the threads
- bool singleThreaded; // set to true for debugging
- };
- /*
- ========================
- idSysWorkerThreadGroup<threadType>::idSysWorkerThreadGroup
- ========================
- */
- template<class threadType>
- ID_INLINE idSysWorkerThreadGroup<threadType>::idSysWorkerThreadGroup( const char * name,
- int numThreads, xthreadPriority priority, int stackSize ) {
- runOneThreadInline = ( numThreads < 0 );
- singleThreaded = false;
- numThreads = abs( numThreads );
- for( int i = 0; i < numThreads; i++ ) {
- threadType *thread = new (TAG_THREAD) threadType;
- thread->StartWorkerThread( va( "%s_worker%i", name, i ), (core_t) i, priority, stackSize );
- threadList.Append( thread );
- }
- }
- /*
- ========================
- idSysWorkerThreadGroup<threadType>::~idSysWorkerThreadGroup
- ========================
- */
- template<class threadType>
- ID_INLINE idSysWorkerThreadGroup<threadType>::~idSysWorkerThreadGroup() {
- threadList.DeleteContents();
- }
- /*
- ========================
- idSysWorkerThreadGroup<threadType>::SignalWorkAndWait
- ========================
- */
- template<class threadType>
- ID_INLINE void idSysWorkerThreadGroup<threadType>::SignalWorkAndWait() {
- if ( singleThreaded ) {
- for( int i = 0; i < threadList.Num(); i++ ) {
- threadList[ i ]->Run();
- }
- return;
- }
- for( int i = 0; i < threadList.Num() - runOneThreadInline; i++ ) {
- threadList[ i ]->SignalWork();
- }
- if ( runOneThreadInline ) {
- threadList[ threadList.Num() - 1 ]->Run();
- }
- for ( int i = 0; i < threadList.Num() - runOneThreadInline; i++ ) {
- threadList[ i ]->WaitForThread();
- }
- }
- /*
- ================================================
- idSysThreadSynchronizer, allows a group of threads to
- synchronize with each other half-way through execution.
- idSysThreadSynchronizer sync;
- class idMyWorkerThread : public idSysThread {
- public:
- virtual int Run() {
- // perform first part of the work here
- sync.Synchronize( threadNum ); // synchronize all threads
- // perform second part of the work here
- return 0;
- }
- // specify thread data here
- unsigned int threadNum;
- };
- idSysWorkerThreadGroup<idMyWorkerThread> workers( "myWorkers", 4 );
- for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
- workers.GetThread( i )->threadNum = i;
- }
- for ( ; ; ) {
- for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
- // workers.GetThread( i )-> // setup work for this thread
- }
- workers.SignalWorkAndWait();
- // use results from the worker threads here
- }
- ================================================
- */
- class idSysThreadSynchronizer {
- public:
- static const int WAIT_INFINITE = -1;
- ID_INLINE void SetNumThreads( unsigned int num );
- ID_INLINE void Signal( unsigned int threadNum );
- ID_INLINE bool Synchronize( unsigned int threadNum, int timeout = WAIT_INFINITE );
- private:
- idList< idSysSignal *, TAG_THREAD > signals;
- idSysInterlockedInteger busyCount;
- };
- /*
- ========================
- idSysThreadSynchronizer::SetNumThreads
- ========================
- */
- ID_INLINE void idSysThreadSynchronizer::SetNumThreads( unsigned int num ) {
- assert( busyCount.GetValue() == signals.Num() );
- if ( (int)num != signals.Num() ) {
- signals.DeleteContents();
- signals.SetNum( (int)num );
- for ( unsigned int i = 0; i < num; i++ ) {
- signals[i] = new (TAG_THREAD) idSysSignal();
- }
- busyCount.SetValue( num );
- SYS_MEMORYBARRIER;
- }
- }
- /*
- ========================
- idSysThreadSynchronizer::Signal
- ========================
- */
- ID_INLINE void idSysThreadSynchronizer::Signal( unsigned int threadNum ) {
- if ( busyCount.Decrement() == 0 ) {
- busyCount.SetValue( (unsigned int) signals.Num() );
- SYS_MEMORYBARRIER;
- for ( int i = 0; i < signals.Num(); i++ ) {
- signals[i]->Raise();
- }
- }
- }
- /*
- ========================
- idSysThreadSynchronizer::Synchronize
- ========================
- */
- ID_INLINE bool idSysThreadSynchronizer::Synchronize( unsigned int threadNum, int timeout ) {
- return signals[threadNum]->Wait( timeout );
- }
- #endif // !__THREAD_H__
|