*
* Note: Fast/slow describe the speed of the lock itself.
* SlowLock can greatly speed a program far more than FastLock in some cases.
*
* ============
* Basic locks:
* ============
*
* RushLock: The fastest lock. Implemented as a non-recursive spinlock.
* Appropriate for:
* - Fast self-sufficient routines that NEVER call other routines
* guarded by the same lock.
* **WARNING** WILL DEAD-LOCK IF SAME THREAD HITS THE SAME LOCK!
*
* FastLock: A recursive spinlock.
* Incurs slight overhead because of tracking lock ownersip and lock depth,
* but is safer than RushLock and almost as fast.
* Appropriate for:
* - Fast routines that might call others guarded by the same lock.
*
* SlowLock: A recursive lock that will put contending threads to sleep.
* Despite its name and its higher overhead, in certain situations,
* it can boost net speed if by allowing a CPU to execute another
* thread instead of spinning uselessly as RushLock or FastLock would do.
* Appropriate for:
* - Functions that execute slowly and often stay locked.
*
* NoLock: For compiling a template class without locking.
* An example is Dlist which ordinarily is locked.
*
* =========
* AutoLock:
* =========
*
* AutoLock is intended to lock a method (not a whole class).
* Limited to being an automatic variable of the method it locks.
* AutoLock is based on an underlying basic lock.
* Its ctor and dtor automatically call Lock() and Unlock().
* AutoLock prevents the mistake of forgetting to call Unlock()
* or placing a return before Unlock().
*
* ================
* Applying a lock:
* ================
*
* In general terms, a lock is required in a block of code where
* multiple threads could access the same resource.
*
* Questions that determine how to lock:
* -------------------------------------
* - What needs locking?
* - Single or multiple locks?
* - Which type of locks?
* - Where to place the locks?
*
* IfLocked() vs. TryLock():
* -------------------------
* IfLocked() is a query operation.
* IfLocked() returns true ONLY IF ANOTHER THREAD owns the lock.
* Returns false for a thread that owns the lock
* because a thread shouldn't block itself.
*
* TryLock() will try to acquire the lock.
* If TryLock() succeeds, Unlock() should be called afterwards.
*
* This is wrong. TryLock() is correct:
*
* if ( mLock.IfLocked() )
* {
* mLock.Lock(); // WRONG: not atomic, causes race-condition
* ....
* mLock.Unlock();
* }
*
* if ( mLock.TryLock() )
* {
* ...
* mLock.Unlock(); // ok, must unlock when done
* }
*
* What needs locking?:
* --------------------
*
* A lock may needed for a method, an instance of a class,
* or every instance of class, or a combination of these.
*
* Single or multiple locks?:
* --------------------------
*
* A C++ class may need locks at different levels.
* Method-level, instance-level, and class-level locks may be necessary.
*
* Consider incrementally developing a List class.
* Its first requirement is to let two threads to call Prepend() and Remove().
* Locking those methods with RushLock or FastLock suffices.
* This is method-level locking.
*
* Next its requirements are increased to support traversal by an Iterator.
* Therefore, the whole instance of the list must be locked (instance-level locking).
* A SlowLock is probably appropriate since the iteration leads to processing every item.
*
* Then profiling becomes a requirement in a debug build.
* The list class must track how many items are linked in every instance.
* The count would be stored in a shared class member.
* A class-level lock is needed to lock-out other instances before updating the count.
*
* Which type of lock:
* -------------------
-
* - Decide if a recursive or non-recursive lock is necessary.
* Non-recursive locks are dangerous. Only use them in hi-freq simple routines.
* - If recursive, select FastLock or SlowLock if a method executes fast or slow, resp.
* SlowLock can provide more net speed by allowing the OS to switch a CPU to a ready thread.
*
* Where to place the locks?
* -------------------------
*
* Rules for lock placement:
* 1. Basic locks must be placed in persistent memory.
* 2. AutoLock objects must be placed on call stack (as an automatic var of a method).
*
* Deadlock versus locking bottlenecks:
* ------------------------------------
* What may appear to be caused by deadlock may be a locking bottleneck instead.
* An actual example: Random class had a FastLock while up to 256 threads were run
* to generate terrain colormap textures. Texels were computed using Random.
* That of course resulted in extreme lock contention at Random, which falsely
* appeared to be a deadlock problem (solved by using rand_r() which needs no locking).
*
* System notes:
* -------------
*
* GNU glibc (2.5+) has an extremely fast pthread_self() function
* which compiles as a "mov reg, gs:[tid]" x86 instruction.
*
*