libc-canlock-wakeup: a failed canlock lets two processes hold one Lock
sys/src/libc/port/lock.c builds Lock from a counter, key, and a
kernel semaphore, sem. lock adds one to key and sleeps on sem if
the lock was taken; unlock subtracts one and releases sem if anyone
is waiting.
canlock adds one to key and, when the lock is taken, undoes the
increment. If the lock is still taken it then releases sem. No
process is waiting for that release; the semaphore keeps the extra
count, and the next lock that has to wait takes it and enters
while the holder is still inside. Every failed canlock on a held
lock leaves one such count behind.
Programs that call canlock on a lock they already hold, as a check,
leak a count on every call. ndb/dns does this on its cache lock;
two workers then changed the cache at once and one of them aborted.
The fix undoes the increment and, in the one case where the lock
was released meanwhile, takes back the wakeup that unlock released
because of the increment. canlock now leaves sem as it found it.
In that case canlock waits until the releasing process has finished
unlock; it never waits for the lock itself. lock(2) calls canlock
non-blocking; this brief wait is the one exception.
The APE copies in sys/src/ape/lib/ap/*/lock.c have the same code
and get the same change.
Apply
cd /
ape/patch -p0 < /path/to/libc-canlock-wakeup/libc-canlock-wakeup.diff
Rebuild
cd /sys/src/libc && mk install
cd /sys/src/ape/lib/ap && mk install
Programs are linked statically: each one that uses Lock,
directly or through QLock, RWLock or libthread, keeps the old
code until it is rebuilt.
|