|  | 
 NAME     
 |  |  |  | lock, canlock, unlock, qlock, canqlock, qunlock, rlock, canrlock,
    runlock, wlock, canwlock, wunlock, rsleep, rwakeup, rwakeupall
    incref, decref – spin locks, queueing rendezvous locks, reader-writer
    locks, rendezvous points, and reference counts 
 | 
 SYNOPSIS     
 |  |  |  | #include <u.h> #include <libc.h>
 void    lock(Lock *l)
 int    canlock(Lock *l)
 void    unlock(Lock *l)
 void    qlock(QLock *l)
 int    canqlock(QLock *l)
 void    qunlock(QLock *l)
 void    rlock(RWLock *l)
 int    canrlock(RWLock *l)
 void    runlock(RWLock *l)
 void    wlock(RWLock *l)
 int    canwlock(RWLock *l)
 void    wunlock(RWLock *l)
 typedef struct Rendez {
 } Rendez;
 void    rsleep(Rendez *r)
 int    rwakeup(Rendez *r)
 int    rwakeupall(Rendez *r)
 #include <thread.h>
 typedef struct Ref {
 } Ref;
 void incref(Ref*)
 long decref(Ref*)
 
 | 
 DESCRIPTION     
 |  |  |  | These routines are used to synchronize processes sharing memory.
    
    
    
    Locks are spin locks, QLocks and RWLocks are different types of
    queueing locks, and Rendezes are rendezvous points. 
    
    
    Locks and rendezvous points have trivial implementations in programs
    not using the thread library (see thread(3)), since such programs
    have no concurrency. 
    
    
    Used carelessly, spin locks can be expensive and can easily generate
    deadlocks. Their use is discouraged, especially in programs that
    use the thread library because they prevent context switches between
    threads. 
    
    
    Lock blocks until the lock has been obtained. Canlock is non-blocking.
    It tries to obtain a lock and returns a non-zero value if it was
    successful, 0 otherwise. Unlock releases a lock. 
    
    
    QLocks have the same interface but are not spin locks; instead
    if the lock is taken qlock will suspend execution of the calling
    thread until it is released. 
    
    
    Although Locks are the more primitive lock, they have limitations;
    for example, they cannot synchronize between tasks in the same
    proc. Use QLocks instead. 
    
    
    RWLocks manage access to a data structure that has distinct readers
    and writers. Rlock grants read access; runlock releases it. Wlock
    grants write access; wunlock releases it. Canrlock and canwlock
    are the non-blocking versions. There may be any number of simultaneous
    readers, but only one writer. Moreover, if write access is granted
    no one
    may have read access until write access is released. 
    
    
    All types of lock should be initialized to all zeros before use;
    this puts them in the unlocked state. 
    
    
    Rendezes are rendezvous points. Each Rendez r is protected by
    a QLock r−>l, which must be held by the callers of rsleep, rwakeup,
    and rwakeupall. Rsleep atomically releases r−>l and suspends execution
    of the calling task. After resuming execution, rsleep will reacquire
    r−>l before returning. If any processes are sleeping on r, rwakeup
    wakes
    one of them. It returns 1 if a process was awakened, 0 if not.
    Rwakeupall wakes all processes sleeping on r, returning the number
    of processes awakened. Rwakeup and rwakeupall do not release r−>l
    and do not suspend execution of the current task. 
    
    
    Before use, Rendezes should be initialized to all zeros except
    for r−>l pointer, which should point at the QLock that will guard
    r. 
    
    
    A Ref contains a long that can be incremented and decremented
    atomically: Incref increments the Ref in one atomic operation.
    Decref atomically decrements the Ref and returns zero if the resulting
    value is zero, non-zero otherwise. 
 | 
 SOURCE     
 BUGS     
 |  |  |  | Locks are not always spin locks. Instead they are usually implemented
    using the pthreads library’s pthread_mutex_t, whose implementation
    method is not defined. 
    
    
    On pthreads-based systems, the implementation of Lock never calls
    pthread_mutex_destroy to free the pthread_mutex_t’s. This leads
    to resource leaks on FreeBSD 5 (though not on Linux 2.6, where
    pthread_mutex_destroy is a no-op).  
    
    
    On systems that do not have a usable pthreads implementation,
    the Lock implementation provided by libthread is still not exactly
    a spin lock. After each unsuccessful attempt, lock calls sleep(0)
    to yield the CPU; this handles the common case where some other
    process holds the lock. After a thousand unsuccessful attempts,
    lock sleeps for 100ms
    between attempts. Another another thousand unsuccessful attempts,
    lock sleeps for a full second between attempts. Locks are not
    intended to be held for long periods of time. The 100ms and full
    second sleeps are only heuristics to avoid tying up the CPU when
    a process deadlocks. As discussed above, if a lock is to be held
    for much more than a
    few instructions, the queueing lock types should be almost always
    be used. 
 | 
 |  |