glenda.party
term% ls -F
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)