shithub: mc

ref: 1edee0e32036f4d1cb23e94cab7b8f72f6ee9193
dir: /lib/thread/mutex+linux.myr/

View raw version
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 Lockedcontended = 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: we set the lock _state to sleep */
	if c == Locked
		c = xchg(&mtx._state, Lockedcontended)
	;;

	while c != Unlocked
		sys.futex(&mtx._state, sys.Futexwait | sys.Futexpriv, Lockedcontended, Zptr, Zptr, 0)
		c = xchg(&mtx._state, Lockedcontended)
	;;
}

const mtxtrylock = {mtx
	-> xcas(&mtx._state, Unlocked, Locked) == Unlocked
}

const mtxunlock = {mtx
	/* uncontended sleep means we can just unlock and move on */
	if mtx._state == Lockedcontended
		mtx._state = Unlocked
	elif xchg(&mtx._state, Unlocked) == Locked
		->
	;;

	for var i = 0; i < nspin; i++
		if mtx._state != Unlocked
			/* there might have been waiters, but we set the _state to unlocked */
			if xcas(&mtx._state, Locked, Lockedcontended) == Contendedlock
				->
			;;
		;;
		relax()
	;;

	sys.futex(&mtx._state, sys.Futexwake | sys.Futexpriv, 1, Zptr, Zptr, 0)
}

const relax = {
}