ref: 74389e559a91db698364664797e3819302a15836
dir: /lib/thread/mutex+freebsd.myr/
use std
use sys
use "atomic.use"
use "common.use"
pkg thread =
type mutex = struct
_state : uint32
;;
const mkmtx : (-> mutex)
const mtxlock : (mtx : mutex# -> void)
const mtxtrylock : (mtx : mutex# -> bool)
const mtxunlock : (mtx : mutex# -> void)
pkglocal const Unlocked = 0
pkglocal const Locked = 1
pkglocal 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
->
;;
;;
/*
Contended case: we set the lock state to Contended. This indicates that there
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
sys.umtx_op( \
&mtx._state castto(void#), \
sys.Umtxwaituintpriv, \
Contended castto(uint64), \
Zptr, Zptr)
c = xchg(&mtx._state, Contended)
;;
}
const mtxtrylock = {mtx
-> xcas(&mtx._state, Unlocked, Locked) == Unlocked
}
const mtxunlock = {mtx
/*
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 mtx._state == Contended
mtx._state = Unlocked
elif xchg(&mtx._state, Unlocked) == Locked
->
;;
/* wake all threads: for some reason nwake */
sys.umtx_op(&mtx._state castto(void#), sys.Umtxwakepriv, 1, Zptr, Zptr)
}