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__