base_thread_lock.hh

Go to the documentation of this file.
00001 /*
00145  * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
00146  *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
00147  *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
00148  *****************************************************************************/
00149 
00150 #if COMPILE_THREADS
00151 #ifndef BASE_THREAD_LOCK_HH
00152 #define BASE_THREAD_LOCK_HH 1
00153 
00154 #include "base_thread_common.hh"
00155 
00156 namespace base {
00157 
00158 // Method-level lock.  It applies to all instances however.
00159 // (THREAD_AUTO_RUSH_LOCK omitted since usually it's wrong as a method-level lock.)
00160 #define THREAD_AUTO_FAST_LOCK {{ PERSISTENT FastLock lock; FastLock::Auto autolock(&lock); }}
00161 #define THREAD_AUTO_SLOW_LOCK {{ PERSISTENT SlowLock lock; SlowLock::Auto autolock(&lock); }}
00162 
00163 #if CPU_X86
00164 #define PAUSE_CPU() asm volatile ("rep;nop": : :"memory");  // Linux kernel 2.6
00165 #else
00166 #define PAUSE_CPU()
00167 #endif
00168 
00178 template<typename LOCK>
00179 class AutoLock
00180 {
00181 
00182 public:
00183     AutoLock( LOCK* lock )
00184     :   mLock(lock)
00185     {
00186     ASSERT_STACK_OBJECT( this );        // AutoLock must be on call stack
00187     ASSERT_PERSISTENT_OBJECT( mLock );  // underlying Slow/FastLock must be persistent
00188 
00189         mLock->Lock();
00190     }
00191 
00192     ~AutoLock()
00193     {
00194         mLock->Unlock();
00195     }
00196 
00197 private:
00198     LOCK*  mLock;  // pointer to underlying lock in PERSISTENT storage (accessible by any thread)
00199 };
00200 
00206 class RushLock : private Atomic
00207 {
00208 
00209 public:
00210     RushLock( void )
00211     :   mLock(false)
00212     {
00213         ASSERT_PERSISTENT_OBJECT( this );  // cannot be on stack
00214     }
00215 
00216     ~RushLock()
00217     {
00218         // NOP
00219     }
00220 
00221     inline void Lock( void )
00222     {
00223         // Not a while loop as compiler would unoptimally predict branch-taken.
00224 
00225         Spin: if ( UX(Atomic::Xchg(&mLock,true)) ) { PAUSE_CPU(); goto Spin; }
00226     }
00227 
00228     inline void Unlock( void )
00229     {
00230         Atomic::Set( &mLock, false );
00231     }
00232 
00233   //bool IfLocked( void ) // N/A since RushLock doesn't track ownership
00234 
00236     class Auto : public AutoLock<RushLock>
00237     {
00238     public:
00239         Auto( RushLock* lock ) : AutoLock<RushLock>(lock) { }
00240     };
00241 
00242 private:
00243     volatile Atomic::Bool  mLock;
00244 };
00245 
00255 class FastLock : private Atomic
00256 {
00257 
00258 private:
00259     enum { UNLOCKED, LOCKED, BUSY };  
00260 
00261 public:
00262     FastLock( void )
00263     :   mLock(UNLOCKED),
00264         mDepth(0),
00265         mOwner(0)  // 0 is ok even if mOwner's type is pthread_t
00266     {
00267         ASSERT_PERSISTENT_OBJECT( this );  // cannot be on stack
00268     }
00269 
00270     ~FastLock() { }
00271 
00272     // FastLock would severely impede system if it never yields CPU.
00273     #define FAST_LOCK_YIELD( i )                                                        \
00274     {                                                                                   \
00275         PAUSE_CPU();                                                                    \
00276         ++i;                                                                            \
00277         if ( UX( i > base::defs::FAST_LOCK_LOOP_YIELD ) )                               \
00278         {                                                                               \
00279             /* pragma: Thread depends on Lock so use THREAD_YIELD() macro. */           \
00280             i = 0;                                                                      \
00281             THREAD_YIELD();                                                             \
00282         }                                                                               \
00283     }
00284 
00285 
00287     void Lock( void )
00288     {
00289         uint i = 0;
00290         while ( true )
00291         {
00292             FAST_LOCK_YIELD(i);
00293 
00294             // BUSY means busy testing the lock.
00295             const int lock = Atomic::Xchg( &mLock, BUSY );  // ok returning to plain int
00296             if ( EX( lock == UNLOCKED ) )
00297             {
00298                 // Was unlocked.  Take the lock.
00299                 ASSERT( mDepth == 0 );
00300                 Atomic::Set( &mDepth, 1 );
00301                 Atomic::Set( &mOwner, THREAD_ATOMIC_TID() );
00302                 Atomic::Set( &mLock, LOCKED );
00303                 break;
00304             }
00305             else if ( UX( lock == LOCKED ) )
00306             {
00307                 // Either this thread is the owner or a competitor.
00308                 if ( THREAD_ATOMIC_TID_EQUAL( mOwner ) )
00309                 {
00310                     // This thread owns the lock.  Increase lock depth.
00311                     Atomic::Add( &mDepth, +1 );
00312                     Atomic::Set( &mLock, LOCKED );
00313                     break;
00314                 }
00315                 else
00316                 {
00317                     // This thread is a competitor.  Revert the lock.
00318                     Atomic::Set( &mLock, LOCKED );
00319                     continue;
00320                 }
00321             }
00322             else  // lock == BUSY
00323             {
00324                 PAUSE_CPU();
00325                 continue;
00326             }
00327         }
00328     }
00329 
00331     void Unlock( void )
00332     {
00333     ASSERT( mLock != UNLOCKED );
00334     ASSERT( mDepth > 0 );
00335     ASSERT( THREAD_ATOMIC_TID_EQUAL( mOwner ) );
00336 
00337         // mLock == BUSY if another thread owns the lock.
00338         // This thread will spin by exchanging BUSY with BUSY
00339         // until the other thread has released the lock.
00340 
00341         uint i = 0;
00342         while ( true )
00343         {
00344             if ( EX( Atomic::Xchg(&mLock,BUSY) != BUSY ) )
00345             {
00346                 // Relinquish lock only when depth falls to zero.
00347                 const int depth = Atomic::Add( &mDepth, -1 );  // regular int ok and faster
00348                 Atomic::Set( &mLock, (depth ? LOCKED : UNLOCKED) );
00349                 break;
00350             }
00351             else  // BUSY
00352             {
00353                 // Another thread is testing the lock.  Wait for it to restore ownership.
00354                 FAST_LOCK_YIELD(i);
00355                 continue;
00356             }
00357         }
00358     }
00359 
00362     bool IfLocked( void )
00363     {
00364         // mOwner might change inside this line but this thread won't be the owner (ok).
00365 
00366         return mLock && not THREAD_ATOMIC_TID_EQUAL(mOwner);
00367     }
00368 
00370     class Auto : public AutoLock<FastLock>
00371     {
00372     public:
00373         Auto( FastLock* lock ) : AutoLock<FastLock>(lock) { }
00374     };
00375 
00376 private:
00377     // volatile is critical to prevent compiler reordering instructions.
00378     // gcc -O2 or -O3 enable -fschedule-insns2.
00379     // (Atomic typedefs are volatile too despite redundancy.)
00380     volatile Atomic::Int        mLock;   // true if locked, false if unlocked
00381     volatile Atomic::Int        mDepth;  // tracks recursion
00382     volatile Thread::AtomicTid  mOwner;  // initialized when first thread takes the lock
00383 };
00384 
00394 class SlowLock
00395 {
00396   // critical
00397 public:
00398             SlowLock( void );
00399             ~SlowLock();
00400     void    Lock( void );
00401     void    Unlock( void );
00402     bool    IfLocked( void );  // query only
00403     bool    TryLock( void );   // tries to take lock
00404 
00406     class Auto : public AutoLock<SlowLock>
00407     {
00408     public:
00409         Auto( SlowLock* lock ) : AutoLock<SlowLock>(lock) { }
00410     };
00411 
00412 private:
00413     pthread_mutex_t mMutex;     
00414 };
00415 
00419 class NoLock
00420 {
00421 
00422 public:
00423     // This code is guaranteed to have no bugs ;-)
00424 
00425             NoLock( void ) { }
00426             ~NoLock() { }
00427     void    Lock( void ) { }
00428     void    Unlock( void ) { }
00429 
00431     class Auto
00432     {
00433     public:
00434         Auto( UNUSED NoLock* ) { }
00435         ~Auto() { }
00436     };
00437 };
00438 
00439 } // namespace base
00440 
00441 #endif // BASE_THREAD_LOCK_HH
00442 #endif // COMPILE_THREADS
Palomino 3D Engine documents generated by doxygen 1.5.3 on Fri Nov 23 11:26:07 2007