LOCK(2)                                                   LOCK(2)

          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

          #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 {
               QLock *l;
          } Rendez;

          void rsleep(Rendez *r)
          int  rwakeup(Rendez *r)
          int  rwakeupall(Rendez *r)

          #include <thread.h>

          typedef struct Ref {
               long ref;
          } Ref;

          void incref(Ref*)
          long decref(Ref*)

          These routines are used  to synchronize processes sharing

          Locks are spin locks, QLocks and RWLocks are different types

     Page 1                       Plan 9             (printed 7/23/24)

     LOCK(2)                                                   LOCK(2)

          of queueing rendezvous locks, and Rendezes are rendezvous

          Locks and rendezvous points work in regular programs as well
          as programs that use the thread library (see thread(2)). The
          thread library replaces the rendezvous(2) system call with
          its own implementation, threadrendezvous, so that threads as
          well as processes may be synchronized by locking calls in
          threaded programs.

          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 task until it is released.

          Although Locks are the more primitive lock, they have limi-
          tations; 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

     Page 2                       Plan 9             (printed 7/23/24)

     LOCK(2)                                                   LOCK(2)

          except for r->l pointer, which should point at the QLock
          that will guard r. It is important that this QLock is the
          same one that protects the rendezvous condition; see the

          A Ref contains a long that can be incremented and decre-
          mented 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.

          Implement a buffered single-element channel using rsleep and

               typedef struct Chan
                   QLock l;
                   Rendez full, empty;
                   int val, haveval;
               } Chan;

                   Chan *c;

                   c = mallocz(sizeof *c, 1);
                   c->full.l = &c->l;
                   c->empty.l = &c->l;
                   return c;

               send(Chan *c, int val)
                   c->haveval = 1;
                   c->val = val;
                   rwakeup(&c->empty);  /* no longer empty */

               recv(Chan *c)
                   int v;


     Page 3                       Plan 9             (printed 7/23/24)

     LOCK(2)                                                   LOCK(2)

                   c->haveval = 0;
                   v = c->val;
                   rwakeup(&c->full);  /* no longer full */
                   return v;

          Note that the QLock protecting the Chan is the same QLock
          used for the Rendez; this ensures that wakeups are not


          rfork in fork(2)

          Locks are not strictly spin locks.  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.  After 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 dis-
          cussed above, if a lock is to be held for much more than a
          few instructions, the queueing lock types should almost
          always be used.

          It is an error for a program to fork when it holds a lock in
          shared memory, since this will result in two processes hold-
          ing the same lock at the same time, which should not happen.

     Page 4                       Plan 9             (printed 7/23/24)