|  | .TH LOCK 3 | 
|  | .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 locks, | 
|  | and | 
|  | .B Rendezes | 
|  | are rendezvous points. | 
|  | .PP | 
|  | Locks and rendezvous points have trivial implementations in programs | 
|  | not using the thread library | 
|  | (see | 
|  | .IR thread (3)), | 
|  | since such programs have no concurrency. | 
|  | .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 thread 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 . | 
|  | .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 SOURCE | 
|  | .B \*9/src/lib9/qlock.c | 
|  | .br | 
|  | .B \*9/src/libthread | 
|  | .SH BUGS | 
|  | .B Locks | 
|  | are not always spin locks. | 
|  | Instead they are usually implemented using the | 
|  | .I pthreads | 
|  | library's | 
|  | .BR pthread_mutex_t , | 
|  | whose implementation method is not defined. | 
|  | .PP | 
|  | On | 
|  | .IR pthreads -based | 
|  | systems, the implementation of | 
|  | .B Lock | 
|  | never calls | 
|  | .I pthread_mutex_destroy | 
|  | to free the | 
|  | .BR pthread_mutex_t 's. | 
|  | This leads to resource leaks on FreeBSD 5 | 
|  | (though not on Linux 2.6, where | 
|  | .I pthread_mutex_destroy | 
|  | is a no-op). | 
|  | .BR | 
|  | .PP | 
|  | On systems that do not have a usable | 
|  | .I pthreads | 
|  | implementation, the | 
|  | .B Lock | 
|  | implementation provided by | 
|  | .I libthread | 
|  | is still not exactly a spin lock. | 
|  | 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. | 
|  | Another 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. |