.TH LOCK 2.SH NAMElock, 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.SH SYNOPSIS.ft L.nf#include <u.h>#include <libc.h>.PP.ft L.nfvoid lock(Lock *l)int canlock(Lock *l)void unlock(Lock *l).PP.ft L.nfvoid qlock(QLock *l)int canqlock(QLock *l)void qunlock(QLock *l).PP.ft L.nfvoid rlock(RWLock *l)int canrlock(RWLock *l)void runlock(RWLock *l).PP.ft L.nfvoid wlock(RWLock *l)int canwlock(RWLock *l)void wunlock(RWLock *l).PP.ft L.nftypedef struct Rendez { QLock *l; \fI...\fP} Rendez;.PP.ft L.nfvoid rsleep(Rendez *r)int rwakeup(Rendez *r)int rwakeupall(Rendez *r).PP.ft L#include <thread.h>.PP.ft L.nftypedef struct Ref { long ref;} Ref;.PP.ft L.nfvoid incref(Ref*)long decref(Ref*).fi.SH DESCRIPTIONThese routines are used to synchronize processes sharing memory..PP.B Locksare spin locks,.B QLocksand.B RWLocksare different types of queueing rendezvous locks,and.B Rendezesare rendezvous points..PPLocks and rendezvous points work in regular programs aswell as programs that use the thread library(see.IR thread (2)).The thread library replaces the.IR rendezvous (2)system callwith its own implementation,.IR threadrendezvous ,so that threads as well as processes may be synchronized by locking callsin threaded programs..PPUsed carelessly, spin locks can be expensive and can easily generate deadlocks.Their use is discouraged, especially in programs that use thethread library because they prevent context switches between threads..PP.I Lockblocks until the lock has been obtained..I Canlockis non-blocking.It tries to obtain a lock and returns a non-zero value if itwas successful, 0 otherwise..I Unlockreleases a lock..PP.B QLockshave the same interface but are not spin locks; instead if the lock is taken.I qlockwill suspend execution of the calling task until it is released..PPAlthough.B Locksare the more primitive lock, they have limitations; for example,they cannot synchronize between tasks in the same.IR proc .Use.B QLocksinstead..PP.B RWLocksmanage access to a data structure that has distinct readers and writers..I Rlockgrants read access;.I runlockreleases it..I Wlockgrants write access;.I wunlockreleases it..I Canrlockand.I canwlockare 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 haveread access until write access is released..PPAll types of lock should be initialized to all zeros before use; thisputs them in the unlocked state..PP.B Rendezesare rendezvous points. Each.B Rendez.I ris protected by a.B QLock.IB r -> l \fR,which must be held by the callers of.IR rsleep ,.IR rwakeup ,and.IR rwakeupall ..I Rsleepatomically releases.IB r -> land suspends execution of the calling task.After resuming execution,.I rsleepwill reacquire.IB r -> lbefore returning.If any processes are sleeping on.IR r ,.I rwakeupwakes one of them.it returns 1 if a process was awakened, 0 if not..I Rwakeupallwakes all processes sleeping on.IR r ,returning the number of processes awakened..I Rwakeupand.I rwakeupalldo not release.IB r -> land do not suspend execution of the current task..PPBefore use,.B Rendezesshould be initialized to all zeros except for.IB r -> lpointer, which should point at the.B QLockthat will guard.IR r .It is important that this.B QLockis the same one that protects the rendezvous condition; see the example..PPA.B Refcontains a.B longthat can be incremented and decremented atomically:.I Increfincrements the.I Refin one atomic operation..I Decrefatomically decrements the.B Refand returns zero if the resulting value is zero, non-zero otherwise..SH EXAMPLEImplement a buffered single-element channel using .I rsleepand.IR rwakeup :.IP.EX.ta +4n +4n +4ntypedef struct Chan{ QLock l; Rendez full, empty; int val, haveval;} Chan;.EE.IP.EX.ta +4n +4n +4nChan*mkchan(void){ Chan *c; c = mallocz(sizeof *c, 1); c->full.l = &c->l; c->empty.l = &c->l; return c;}.EE.IP.EX.ta +4n +4n +4nvoidsend(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);}.EE.IP.EX.ta +4n +4n +4nintrecv(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;}.EE.LPNote that the .B QLockprotecting the.B Chanis the same.B QLockused for the .BR Rendez ;this ensures that wakeups are not missed..SH SOURCE.B /sys/src/libc/port/lock.c.br.B /sys/src/libc/9sys/qlock.c.br.B /sys/src/libthread/ref.c.SH SEE ALSO.I rforkin.IR fork (2).SH BUGS.B Locksare not strictly spin locks.After each unsuccessful attempt,.I lockcalls.B sleep(0)to yield the CPU; this handles the common casewhere some other process holds the lock.After a thousand unsuccessful attempts,.I locksleeps for 100ms between attempts.After another thousand unsuccessful attempts,.I locksleeps for a full second between attempts..B Locksare not intended to be held for long periods of time.The 100ms and full second sleeps are only heuristics toavoid 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..PPIt is an error for a program to.I forkwhen it holds a lock in shared memory, since this will resultin two processes holding the same lock at the same time,which should not happen.
.TH LOCK 2
.SH 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
.SH SYNOPSIS
.ft L
.nf
#include <u.h>
#include <libc.h>
.PP
.ft L
.nf
void lock(Lock *l)
int canlock(Lock *l)
void unlock(Lock *l)
.PP
.ft L
.nf
void qlock(QLock *l)
int canqlock(QLock *l)
void qunlock(QLock *l)
.PP
.ft L
.nf
void rlock(RWLock *l)
int canrlock(RWLock *l)
void runlock(RWLock *l)
.PP
.ft L
.nf
void wlock(RWLock *l)
int canwlock(RWLock *l)
void wunlock(RWLock *l)
.PP
.ft L
.nf
typedef struct Rendez {
QLock *l;
\fI...\fP
} Rendez;
.PP
.ft L
.nf
void rsleep(Rendez *r)
int rwakeup(Rendez *r)
int rwakeupall(Rendez *r)
.PP
.ft L
#include <thread.h>
.PP
.ft L
.nf
typedef struct Ref {
long ref;
} Ref;
.PP
.ft L
.nf
void incref(Ref*)
long decref(Ref*)
.fi
.SH DESCRIPTION
These routines are used to synchronize processes sharing memory.
.PP
.B Locks
are spin locks,
.B QLocks
and
.B RWLocks
are different types of queueing rendezvous locks,
and
.B Rendezes
are rendezvous points.
.PP
Locks and rendezvous points work in regular programs as
well as programs that use the thread library
(see
.IR thread (2)).
The thread library replaces the
.IR rendezvous (2)
system call
with its own implementation,
.IR threadrendezvous ,
so that threads as well as processes may be synchronized by locking calls
in threaded programs.
.PP
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.
.PP
.I Lock
blocks until the lock has been obtained.
.I Canlock
is non-blocking.
It tries to obtain a lock and returns a non-zero value if it
was successful, 0 otherwise.
.I Unlock
releases a lock.
.PP
.B QLocks
have the same interface but are not spin locks; instead if the lock is taken
.I qlock
will suspend execution of the calling task until it is released.
.PP
Although
.B Locks
are the more primitive lock, they have limitations; for example,
they cannot synchronize between tasks in the same
.IR proc .
Use
.B QLocks
instead.
.PP
.B RWLocks
manage access to a data structure that has distinct readers and writers.
.I Rlock
grants read access;
.I runlock
releases it.
.I Wlock
grants write access;
.I wunlock
releases it.
.I Canrlock
and
.I 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.
.PP
All types of lock should be initialized to all zeros before use; this
puts them in the unlocked state.
.PP
.B Rendezes
are rendezvous points. Each
.B Rendez
.I r
is protected by a
.B QLock
.IB r -> l \fR,
which must be held by the callers of
.IR rsleep ,
.IR rwakeup ,
and
.IR rwakeupall .
.I Rsleep
atomically releases
.IB r -> l
and suspends execution of the calling task.
After resuming execution,
.I rsleep
will reacquire
.IB r -> l
before returning.
If any processes are sleeping on
.IR r ,
.I rwakeup
wakes one of them.
it returns 1 if a process was awakened, 0 if not.
.I Rwakeupall
wakes all processes sleeping on
.IR r ,
returning the number of processes awakened.
.I Rwakeup
and
.I rwakeupall
do not release
.IB r -> l
and do not suspend execution of the current task.
.PP
Before use,
.B Rendezes
should be initialized to all zeros except for
.IB r -> l
pointer, which should point at the
.B QLock
that will guard
.IR r .
It is important that this
.B QLock
is the same one that protects the rendezvous condition; see the example.
.PP
A
.B Ref
contains a
.B long
that can be incremented and decremented atomically:
.I Incref
increments the
.I Ref
in one atomic operation.
.I Decref
atomically decrements the
.B Ref
and returns zero if the resulting value is zero, non-zero otherwise.
.SH EXAMPLE
Implement a buffered single-element channel using
.I rsleep
and
.IR rwakeup :
.IP
.EX
.ta +4n +4n +4n
typedef struct Chan
{
QLock l;
Rendez full, empty;
int val, haveval;
} Chan;
.EE
.IP
.EX
.ta +4n +4n +4n
Chan*
mkchan(void)
{
Chan *c;
c = mallocz(sizeof *c, 1);
c->full.l = &c->l;
c->empty.l = &c->l;
return c;
}
.EE
.IP
.EX
.ta +4n +4n +4n
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);
}
.EE
.IP
.EX
.ta +4n +4n +4n
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;
}
.EE
.LP
Note that the
.B QLock
protecting the
.B Chan
is the same
.B QLock
used for the
.BR Rendez ;
this ensures that wakeups are not missed.
.SH SOURCE
.B /sys/src/libc/port/lock.c
.br
.B /sys/src/libc/9sys/qlock.c
.br
.B /sys/src/libthread/ref.c
.SH SEE ALSO
.I rfork
in
.IR fork (2)
.SH BUGS
.B Locks
are not strictly spin locks.
After each unsuccessful attempt,
.I lock
calls
.B sleep(0)
to yield the CPU; this handles the common case
where some other process holds the lock.
After a thousand unsuccessful attempts,
.I lock
sleeps for 100ms between attempts.
After another thousand unsuccessful attempts,
.I lock
sleeps for a full second between attempts.
.B 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.
.PP
It is an error for a program to
.I 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.