ref: 2cd9927f90410e345fab398fe2573fe6971ef3b0
dir: /lib/thread/mutex+futex.myr/
use "atomic"
use "common"
use "futex"
pkg thread =
type mutex = struct
_state : ftxtag
;;
const mkmtx : (-> mutex)
const mtxlock : (mtx : mutex# -> void)
const mtxtrylock : (mtx : mutex# -> bool)
const mtxunlock : (mtx : mutex# -> void)
pkglocal const mtxcontended : (mtx : mutex# -> void)
;;
const Unlocked = 0
const Locked = 1
const Contended = 2
var nspin = 10 /* FIXME: pick a sane number, based on CPU count */
const mkmtx = {
-> [._state = Unlocked]
}
const mtxlock = {mtx
var c
/*
Uncontended case: we get an unlocked mutex, and we lock it.
*/
c = Locked
for var i = 0; i < nspin; i++
c = xcas(&mtx._state, Unlocked, Locked)
if c == Unlocked
-> void
;;
;;
/*
Contended case: we set the lock state to Contended. This indicates that
the lock is locked, and we potentially have threads waiting on it,
which means that we will need to wake them up.
*/
if c == Locked
c = xchg(&mtx._state, Contended)
;;
while c != Unlocked
ftxwait(&mtx._state, Contended, Zptr)
c = xchg(&mtx._state, Contended)
;;
}
const mtxtrylock = {mtx
-> xcas(&mtx._state, Unlocked, Locked) == Unlocked
}
const mtxunlock = {mtx
/*
Either the lock is contended or it's uncontended. Any other
state is a bug.
Uncontended case: If the mutex state is not contended, and we still
are uncontended by the xchg() call, then it's safe to simply return;
nobody was waiting for us.
*/
if xchg(&mtx._state, Unlocked) == Contended
ftxwake(&mtx._state)
;;
}
const mtxcontended = {mtx
while xchg(&mtx._state, Contended) != Unlocked
ftxwait(&mtx._state, Contended, Zptr)
;;
}