LOCK(3)
๐ฆ LOCK(3): synchronization but make it group-chat ๐ฆ
okay so this is LOCK(3) from the Library Functions Manual, and itโs literally the whole lore drop for spin locks, queueing rendezvous locks, reader-writer locks, rendezvous points, and reference counts. stay with me bestie, itโs giving โlow-level but iconicโ ๐
โจ NAME โจ
the whole crew is lock, canlock, unlock, qlock, canqlock, qunlock, rlock, canrlock, runlock, wlock, canwlock, wunlock, rsleep, rwakeup, rwakeupall, incref, and decref โ and they handle spin locks, queueing rendezvous locks, reader-writer locks, rendezvous points, and reference counts.
tiny icon detail: the original NAME line literally smooshes rwakeupall incref together with no comma, so yes, we are preserving that chaos ๐๐
๐ป 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 the social glue for synchronizing processes that share memory, bestie. no cap.
Locks are spin locks. QLocks and RWLocks are different types of queueing locks. Rendezes are rendezvous points. yes, the naming is A Lot, but we move ๐
Locks and rendezvous points have trivial implementations in programs that are not using the thread library (peep thread(3)), because those programs have no concurrency. cute for them.
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. so likeโฆ be normal about them.
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 successful, otherwise 0. unlock releases a lock. simple, but the way it matters ๐ซถ
QLocks have the same interface but are not spin locks. instead, if the lock is taken, qlock will suspend execution of the calling thread until it is released. queueing queen behavior.
even though Locks are the more primitive lock, they have limitations โ for example, they cannot synchronize between tasks in the same proc. use QLocks instead. thatโs the tea โ
RWLocks manage access to a data structure that has distinct readers and writers:
rlockgrants read access;runlockreleases it.wlockgrants write access;wunlockreleases it.canrlockandcanwlockare 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. strict but fair ๐
all types of lock should be initialized to all zeros before use; this puts them in the unlocked state. do not skip this step, iโm begging ๐
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 rwakeupall.
rsleep atomically releases r->l and suspends execution of the calling task. after resuming execution, rsleep will reacquire r->l before returning. the way it locks, releases, and locks againโฆ main character energy ๐ฅ
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 suspend execution of the current task.
before use, Rendezes should be initialized to all zeros except for the r->l pointer, which should point at the QLock that will guard r. yes, that one pointer is the exception. sheโs special ๐
a Ref contains a long that can be incremented and decremented atomically:
increfincrements theRefin one atomic operation.decrefatomically decrements theRefand returns zero if the resulting value is zero, non-zero otherwise.
atomic reference-counting realness. obsessed.
๐ SOURCE ๐
/src/lib9/qlock.c/src/libthread
๐ BUGS ๐
Locks are not always spin locks. instead, they are usually implemented using the pthreads libraryโs pthread_mutex_t, whose implementation method is not defined. so likeโฆ mystery behavior, but make it pthreads.
on pthreads-based systems, the implementation of Lock never calls pthread_mutex_destroy to free the pthread_mutex_tโs. this leads to resource leaks on FreeBSD 5 (though not on Linux 2.6, where pthread_mutex_destroy is a no-op). leaks are not slay ๐
on systems that do not have a usable pthreads implementation, the Lock implementation provided by libthread is still not exactly a spin lock. 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 another thousand unsuccessful attempts, lock sleeps for a full second between attempts. (yes, the page says โanother anotherโ ๐ญ we are respecting the lore.)
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. (their grammar, not mine, bestie ๐ซถ)
๐ anyway, thatโs LOCK(3) from Library Functions Manual โ the footer is just LOCK(3) again and thereโs no date or version stamp on this one, so bye bestie ๐
๐ฆ where this page comes from
- alpine 3.24: plan9port-doc 0_git20260208-r0 ยท LPL-1.02
- arch rolling: plan9port 20261021-2 ยท custom
- nixos 26.05: plan9port 0-unstable-2025-11-10
- nixos 26.05: plan9port-wayland 0-unstable-2025-11-26
- nixpkgs unstable: plan9port 0-unstable-2025-11-10
- nixpkgs unstable: plan9port-wayland 0-unstable-2025-11-26
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms