ref: d3091cbc968337bdd47efe3bab7e752972cad726
dir: /lib/thread/mutex+linux.myr/
use std
use sys
use "atomic.use"
pkg thread =
type mutex = struct
_state : int32
;;
const mkmtx : (-> mutex)
const mtxlock : (mtx : mutex# -> void)
const mtxtrylock : (mtx : mutex# -> bool)
const mtxunlock : (mtx : mutex# -> void)
;;
const Unlocked = 0
const Locked = 1
const Contended = 2
generic Zptr = 0 castto(@a#)
var nspin = 1 /* 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.
*/
for var i = 0; i < nspin; i++
c = xcas(&mtx._state, Unlocked, Locked)
if c == Unlocked
->
;;
relax()
;;
/*
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.futex(&mtx._state, sys.Futexwait | sys.Futexpriv, Contended, Zptr, Zptr, 0)
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 && xchg(&mtx._state, Unlocked) == Locked
->
;;
/*
Contended case: Now we unlock, and wait a little bit to see if anyone
takes the lock. If someone takes the lock, we can avoid waking entering
the kernel to wake processes. However, we need to set the lock to contended
so that if there are still waiters, the thread that took the lock takes
responsibility for unlocking.
*/
mtx._state = Unlocked
for var i = 0; i < nspin; i++
if mtx._state != Unlocked
/*
If we're locked, set the state to contended to avoid
missed wakes.
*/
if xcas(&mtx._state, Locked, Contended) == Contended
->
;;
;;
relax()
;;
sys.futex(&mtx._state, sys.Futexwake | sys.Futexpriv, 1, Zptr, Zptr, 0)
}
const relax = {
/* FIXME: what is an appropriate way to kill CPU time without entering kernel? */
}