123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- .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.
|