glenda.party
term% ls -F
term% pwd
$home/manuals/9front/2/lock
term% cat index.txt
LOCK(2)                       System Calls Manual                      LOCK(2)

NAME
       lock,  canlock, unlock, qlock, canqlock, qunlock, rlock, canrlock, run‐
       lock, 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 {
            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*)

DESCRIPTION
       These routines are used  to synchronize processes sharing memory.

       Locks are spin locks, QLocks and RWLocks are different types of  queue‐
       ing rendezvous locks, and Rendezes are rendezvous points.

       Locks  and  rendezvous  points work in regular programs as well as pro‐
       grams 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  synchro‐
       nized 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  suc‐
       cessful, 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 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 rwakeu‐
       pall.  Rsleep atomically releases r->l and suspends  execution  of  the
       calling task.  After resuming execution, rsleep will reacquire r->l be‐
       fore  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 sus‐
       pend 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.  It is  im‐
       portant  that  this  QLock is the same one that protects the rendezvous
       condition; see the example.

       A Ref contains a long that can be incremented  and  decremented  atomi‐
       cally: Incref increments the Ref in one atomic operation.  Decref atom‐
       ically  decrements  the  Ref and returns zero if the resulting value is
       zero, non-zero otherwise.

EXAMPLE
       Implement a buffered single-element channel using rsleep and rwakeup:

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

              Chan*
              mkchan(void)
              {
                  Chan *c;

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

              void
              send(Chan *c, int val)
              {
                  qlock(&c->l);
                  while(c->haveval)
                      rsleep(&c->full);
                  c->haveval = 1;
                  c->val = val;
                  rwakeup(&c->empty);  /* no longer empty */
                  qunlock(&c->l);
              }

              int
              recv(Chan *c)
              {
                  int v;

                  qlock(&c->l);
                  while(!c->haveval)
                      rsleep(&c->empty);
                  c->haveval = 0;
                  v = c->val;
                  rwakeup(&c->full);  /* no longer full */
                  qunlock(&c->l);
                  return v;
              }

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

SOURCE
       /sys/src/libc/port/lock.c
       /sys/src/libc/9sys/qlock.c
       /sys/src/libthread/ref.c

SEE ALSO
       rfork in fork(2)

BUGS
       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 thou‐
       sand unsuccessful attempts, lock sleeps for a full second  between  at‐
       tempts.   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.

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

                                                                       LOCK(2)