groff 1.23.0 added .MR to its -man macro package. The NEWS file states
that the inclusion of the macro "was prompted by its introduction to
Plan 9 from User Space's troff in August 2020." From d32deab it seems
that the name for Plan 9 from User Space's implementation was suggested
by groff maintainer G. Brandon Robinson.
Not sure if the intention was to make these definitions compatible, but
it would be nice if they were.
Currently, Plan 9 from User Space's .MR expects its second argument to
be parenthesized. groff's .MR does not. This results in extra
parentheses appearing in manual references when viewing Plan 9 from User
Space's manual pages on a system using groff.
248 lines
4.9 KiB
Groff
248 lines
4.9 KiB
Groff
.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
|
|
.MR 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.
|