DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Thread.h (15349B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 #ifndef __THREAD_H__
     29 #define __THREAD_H__
     30 
     31 /*
     32 ================================================
     33 idSysMutex provides a C++ wrapper to the low level system mutex functions.  A mutex is an
     34 object that can only be locked by one thread at a time.  It's used to prevent two threads
     35 from accessing the same piece of data simultaneously.
     36 ================================================
     37 */
     38 class idSysMutex {
     39 public:
     40 					idSysMutex() { Sys_MutexCreate( handle ); }
     41 					~idSysMutex() { Sys_MutexDestroy( handle ); }
     42 
     43 	bool			Lock( bool blocking = true ) { return Sys_MutexLock( handle, blocking ); }
     44 	void			Unlock() { Sys_MutexUnlock( handle ); }
     45 
     46 private:
     47 	mutexHandle_t	handle;
     48 
     49 					idSysMutex( const idSysMutex & s ) {}
     50 	void			operator=( const idSysMutex & s ) {}
     51 };
     52 
     53 /*
     54 ================================================
     55 idScopedCriticalSection is a helper class that automagically locks a mutex when it's created
     56 and unlocks it when it goes out of scope.
     57 ================================================
     58 */
     59 class idScopedCriticalSection {
     60 public:
     61 	idScopedCriticalSection( idSysMutex & m ) : mutex(&m) { mutex->Lock(); }
     62 	~idScopedCriticalSection() { mutex->Unlock(); }
     63 
     64 private:
     65 	idSysMutex *	mutex;	// NOTE: making this a reference causes a TypeInfo crash
     66 };
     67 
     68 /*
     69 ================================================
     70 idSysSignal is a C++ wrapper for the low level system signal functions.  A signal is an object
     71 that a thread can wait on for it to be raised.  It's used to indicate data is available or that
     72 a thread has reached a specific point.
     73 ================================================
     74 */
     75 class idSysSignal {
     76 public:
     77 	static const int	WAIT_INFINITE = -1;
     78 
     79 			idSysSignal( bool manualReset = false )	{ Sys_SignalCreate( handle, manualReset ); }
     80 			~idSysSignal()	{ Sys_SignalDestroy( handle ); }
     81 
     82 	void	Raise() { Sys_SignalRaise( handle ); }
     83 	void	Clear() { Sys_SignalClear( handle ); }
     84 
     85 	// Wait returns true if the object is in a signalled state and
     86 	// returns false if the wait timed out. Wait also clears the signalled
     87 	// state when the signalled state is reached within the time out period.
     88 	bool	Wait( int timeout = WAIT_INFINITE ) { return Sys_SignalWait( handle, timeout ); }
     89 
     90 private:
     91 	signalHandle_t		handle;
     92 
     93 						idSysSignal( const idSysSignal & s ) {}
     94 	void				operator=( const idSysSignal & s ) {}
     95 };
     96 
     97 /*
     98 ================================================
     99 idSysInterlockedInteger is a C++ wrapper for the low level system interlocked integer
    100 routines to atomically increment or decrement an integer.
    101 ================================================
    102 */
    103 class idSysInterlockedInteger {
    104 public:
    105 						idSysInterlockedInteger() : value( 0 ) {}
    106 
    107 	// atomically increments the integer and returns the new value
    108 	int					Increment() { return Sys_InterlockedIncrement( value ); }
    109 
    110 	// atomically decrements the integer and returns the new value
    111 	int					Decrement() { return Sys_InterlockedDecrement( value ); }
    112 
    113 	// atomically adds a value to the integer and returns the new value
    114 	int					Add( int v ) { return Sys_InterlockedAdd( value, (interlockedInt_t) v ); }
    115 
    116 	// atomically subtracts a value from the integer and returns the new value
    117 	int					Sub( int v ) { return Sys_InterlockedSub( value, (interlockedInt_t) v ); }
    118 
    119 	// returns the current value of the integer
    120 	int					GetValue() const { return value; }
    121 
    122 	// sets a new value, Note: this operation is not atomic
    123 	void				SetValue( int v ) { value = (interlockedInt_t)v; }
    124 
    125 private:
    126 	interlockedInt_t	value;
    127 };
    128 
    129 /*
    130 ================================================
    131 idSysInterlockedPointer is a C++ wrapper around the low level system interlocked pointer
    132 routine to atomically set a pointer while retrieving the previous value of the pointer.
    133 ================================================
    134 */
    135 template< typename T >
    136 class idSysInterlockedPointer {
    137 public:
    138 			idSysInterlockedPointer() : ptr( NULL ) {}
    139 
    140 	// atomically sets the pointer and returns the previous pointer value
    141 	T *		Set( T * newPtr ) { 
    142 				return (T *) Sys_InterlockedExchangePointer( (void * &) ptr, newPtr ); 
    143 			}
    144 
    145 	// atomically sets the pointer to 'newPtr' only if the previous pointer is equal to 'comparePtr'
    146 	// ptr = ( ptr == comparePtr ) ? newPtr : ptr
    147 	T *		CompareExchange( T * comparePtr, T * newPtr ) {
    148 				return (T *) Sys_InterlockedCompareExchangePointer( (void * &) ptr, comparePtr, newPtr );
    149 	}
    150 
    151 	// returns the current value of the pointer
    152 	T *		Get() const { return ptr; }
    153 
    154 private:
    155 	T *		ptr;
    156 };
    157 
    158 /*
    159 ================================================
    160 idSysThread is an abstract base class, to be extended by classes implementing the
    161 idSysThread::Run() method. 
    162 
    163 	class idMyThread : public idSysThread {
    164 	public:
    165 		virtual int Run() {
    166 			// run thread code here
    167 			return 0;
    168 		}
    169 		// specify thread data here
    170 	};
    171 
    172 	idMyThread thread;
    173 	thread.Start( "myThread" );
    174 
    175 A worker thread is a thread that waits in place (without consuming CPU)
    176 until work is available. A worker thread is implemented as normal, except that, instead of
    177 calling the Start() method, the StartWorker() method is called to start the thread.
    178 Note that the Sys_CreateThread function does not support the concept of worker threads.
    179 
    180 	class idMyWorkerThread : public idSysThread {
    181 	public:
    182 		virtual int Run() {
    183 			// run thread code here
    184 			return 0;
    185 		}
    186 		// specify thread data here
    187 	};
    188 
    189 	idMyWorkerThread thread;
    190 	thread.StartThread( "myWorkerThread" );
    191  
    192 	// main thread loop
    193 	for ( ; ; ) {
    194 		// setup work for the thread here (by modifying class data on the thread)
    195 		thread.SignalWork();           // kick in the worker thread
    196 		// run other code in the main thread here (in parallel with the worker thread)
    197 		thread.WaitForThread();        // wait for the worker thread to finish
    198 		// use results from worker thread here
    199 	}
    200 
    201 In the above example, the thread does not continuously run in parallel with the main Thread,
    202 but only for a certain period of time in a very controlled manner. Work is set up for the
    203 Thread and then the thread is signalled to process that work while the main thread continues.
    204 After doing other work, the main thread can wait for the worker thread to finish, if it has not
    205 finished already. When the worker thread is done, the main thread can safely use the results
    206 from the worker thread.
    207 
    208 Note that worker threads are useful on all platforms but they do not map to the SPUs on the PS3.
    209 ================================================
    210 */
    211 class idSysThread {
    212 public:
    213 					idSysThread();
    214 	virtual			~idSysThread();
    215 
    216 	const char *	GetName() const { return name.c_str(); }
    217 	uintptr_t		GetThreadHandle() const { return threadHandle; }
    218 	bool			IsRunning() const { return isRunning; }
    219 	bool			IsTerminating() const { return isTerminating; }
    220 
    221 	//------------------------
    222 	// Thread Start/Stop/Wait
    223 	//------------------------
    224 
    225 	bool			StartThread( const char * name, core_t core, 
    226 								 xthreadPriority priority = THREAD_NORMAL,
    227 								 int stackSize = DEFAULT_THREAD_STACK_SIZE );
    228 
    229 	bool			StartWorkerThread( const char * name, core_t core, 
    230 									   xthreadPriority priority = THREAD_NORMAL,
    231 									   int stackSize = DEFAULT_THREAD_STACK_SIZE );
    232 
    233 	void			StopThread( bool wait = true );
    234 
    235 	// This can be called from multiple other threads. However, in the case
    236 	// of a worker thread, the work being "done" has little meaning if other
    237 	// threads are continuously signalling more work.
    238 	void			WaitForThread();
    239 
    240 	//------------------------
    241 	// Worker Thread
    242 	//------------------------
    243 
    244 	// Signals the thread to notify work is available.
    245 	// This can be called from multiple other threads.
    246 	void			SignalWork();
    247 
    248 	// Returns true if the work is done without waiting.
    249 	// This can be called from multiple other threads. However, the work
    250 	// being "done" has little meaning if other threads are continuously
    251 	// signalling more work.
    252 	bool			IsWorkDone();
    253 
    254 protected:
    255 	// The routine that performs the work.
    256 	virtual int		Run();
    257 
    258 private:
    259 	idStr			name;
    260 	uintptr_t		threadHandle;
    261 	bool			isWorker;
    262 	bool			isRunning;
    263 	volatile bool	isTerminating;
    264 	volatile bool	moreWorkToDo;
    265 	idSysSignal		signalWorkerDone;
    266 	idSysSignal		signalMoreWorkToDo;
    267 	idSysMutex		signalMutex;
    268 
    269 	static int		ThreadProc( idSysThread * thread );
    270 
    271 					idSysThread( const idSysThread & s ) {}
    272 	void			operator=( const idSysThread & s ) {}
    273 };
    274 
    275 /*
    276 ================================================
    277 idSysWorkerThreadGroup implements a group of worker threads that 
    278 typically crunch through a collection of similar tasks.
    279 
    280 	class idMyWorkerThread : public idSysThread {
    281 	public:
    282 		virtual int Run() {
    283 			// run thread code here
    284 			return 0;
    285 		}
    286 		// specify thread data here
    287 	};
    288 
    289 	idSysWorkerThreadGroup<idMyWorkerThread> workers( "myWorkers", 4 );
    290 	for ( ; ; ) {
    291 		for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
    292 			// workers.GetThread( i )-> // setup work for this thread
    293 		}
    294 		workers.SignalWorkAndWait();
    295 		// use results from the worker threads here
    296 	}
    297 
    298 The concept of worker thread Groups is probably most useful for tools and compilers.
    299 For instance, the AAS Compiler is using a worker thread group. Although worker threads
    300 will work well on the PC, Mac and the 360, they do not directly map to the PS3,
    301 in that the worker threads won't automatically run on the SPUs.
    302 ================================================
    303 */
    304 template<class threadType>
    305 class idSysWorkerThreadGroup {
    306 public:
    307 					idSysWorkerThreadGroup( const char * name, int numThreads,
    308 											xthreadPriority priority = THREAD_NORMAL,
    309 											int stackSize = DEFAULT_THREAD_STACK_SIZE );
    310 
    311 	virtual			~idSysWorkerThreadGroup();
    312 
    313 	int				GetNumThreads() const { return threadList.Num(); }
    314 	threadType &	GetThread( int i ) { return *threadList[i]; }
    315 
    316 	void			SignalWorkAndWait();
    317 
    318 private:
    319 	idList<threadType *, TAG_THREAD>	threadList;
    320 	bool					runOneThreadInline;	// use the signalling thread as one of the threads
    321 	bool					singleThreaded;		// set to true for debugging
    322 };
    323 
    324 /*
    325 ========================
    326 idSysWorkerThreadGroup<threadType>::idSysWorkerThreadGroup
    327 ========================
    328 */
    329 template<class threadType>
    330 ID_INLINE idSysWorkerThreadGroup<threadType>::idSysWorkerThreadGroup( const char * name, 
    331 			int numThreads, xthreadPriority priority, int stackSize ) {
    332 	runOneThreadInline = ( numThreads < 0 );
    333 	singleThreaded = false;
    334 	numThreads = abs( numThreads );
    335 	for( int i = 0; i < numThreads; i++ ) {
    336 		threadType *thread = new (TAG_THREAD) threadType;
    337 		thread->StartWorkerThread( va( "%s_worker%i", name, i ), (core_t) i, priority, stackSize );
    338 		threadList.Append( thread );
    339 	}
    340 }
    341 
    342 /*
    343 ========================
    344 idSysWorkerThreadGroup<threadType>::~idSysWorkerThreadGroup
    345 ========================
    346 */
    347 template<class threadType>
    348 ID_INLINE idSysWorkerThreadGroup<threadType>::~idSysWorkerThreadGroup() {
    349 	threadList.DeleteContents();
    350 }
    351 
    352 /*
    353 ========================
    354 idSysWorkerThreadGroup<threadType>::SignalWorkAndWait
    355 ========================
    356 */
    357 template<class threadType>
    358 ID_INLINE void idSysWorkerThreadGroup<threadType>::SignalWorkAndWait() {
    359 	if ( singleThreaded ) {
    360 		for( int i = 0; i < threadList.Num(); i++ ) {
    361 			threadList[ i ]->Run();
    362 		}
    363 		return;
    364 	}
    365 	for( int i = 0; i < threadList.Num() - runOneThreadInline; i++ ) {
    366 		threadList[ i ]->SignalWork();
    367 	}
    368 	if ( runOneThreadInline ) {
    369 		threadList[ threadList.Num() - 1 ]->Run();
    370 	}
    371 	for ( int i = 0; i < threadList.Num() - runOneThreadInline; i++ ) {
    372 		threadList[ i ]->WaitForThread();
    373 	}
    374 }
    375 
    376 /*
    377 ================================================
    378 idSysThreadSynchronizer, allows a group of threads to 
    379 synchronize with each other half-way through execution.
    380 
    381 	idSysThreadSynchronizer sync;
    382 
    383 	class idMyWorkerThread : public idSysThread {
    384 	public:
    385 		virtual int Run() {
    386 			// perform first part of the work here
    387 			sync.Synchronize( threadNum );	// synchronize all threads
    388 			// perform second part of the work here
    389 			return 0;
    390 		}
    391 		// specify thread data here
    392 		unsigned int threadNum;
    393 	};
    394 
    395 	idSysWorkerThreadGroup<idMyWorkerThread> workers( "myWorkers", 4 );
    396 	for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
    397 		workers.GetThread( i )->threadNum = i;
    398 	}
    399 
    400 	for ( ; ; ) {
    401 		for ( int i = 0; i < workers.GetNumThreads(); i++ ) {
    402 			// workers.GetThread( i )-> // setup work for this thread
    403 		}
    404 		workers.SignalWorkAndWait();
    405 		// use results from the worker threads here
    406 	}
    407 
    408 ================================================
    409 */
    410 class idSysThreadSynchronizer {
    411 public:
    412 	static const int	WAIT_INFINITE = -1;
    413 
    414 	ID_INLINE	void			SetNumThreads( unsigned int num );
    415 	ID_INLINE	void			Signal( unsigned int threadNum );
    416 	ID_INLINE	bool			Synchronize( unsigned int threadNum, int timeout = WAIT_INFINITE );
    417 
    418 private:
    419 	idList< idSysSignal *, TAG_THREAD >		signals;
    420 	idSysInterlockedInteger		busyCount;
    421 };
    422 
    423 /*
    424 ========================
    425 idSysThreadSynchronizer::SetNumThreads
    426 ========================
    427 */
    428 ID_INLINE void idSysThreadSynchronizer::SetNumThreads( unsigned int num ) {
    429 	assert( busyCount.GetValue() == signals.Num() );
    430 	if ( (int)num != signals.Num() ) {
    431 		signals.DeleteContents();
    432 		signals.SetNum( (int)num );
    433 		for ( unsigned int i = 0; i < num; i++ ) {
    434 			signals[i] = new (TAG_THREAD) idSysSignal();
    435 		}
    436 		busyCount.SetValue( num );
    437 		SYS_MEMORYBARRIER;
    438 	}
    439 }
    440 
    441 /*
    442 ========================
    443 idSysThreadSynchronizer::Signal
    444 ========================
    445 */
    446 ID_INLINE void idSysThreadSynchronizer::Signal( unsigned int threadNum ) {
    447 	if ( busyCount.Decrement() == 0 ) {
    448 		busyCount.SetValue( (unsigned int) signals.Num() );
    449 		SYS_MEMORYBARRIER;
    450 		for ( int i = 0; i < signals.Num(); i++ ) {
    451 			signals[i]->Raise();
    452 		}
    453 	}
    454 }
    455 
    456 /*
    457 ========================
    458 idSysThreadSynchronizer::Synchronize
    459 ========================
    460 */
    461 ID_INLINE bool idSysThreadSynchronizer::Synchronize( unsigned int threadNum, int timeout ) {
    462 	return signals[threadNum]->Wait( timeout );
    463 }
    464 
    465 #endif // !__THREAD_H__