shithub: mc

Download patch

ref: 77c74d43d5540fd3bb3915d45d786986827ef0f8
parent: e841ef7c45f5ed30368c5070d52e1cf5912a1a41
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Sep 15 20:11:40 EDT 2015

Add mutex implementation.

--- a/lib/thread/atomic-impl+x64.s
+++ b/lib/thread/atomic-impl+x64.s
@@ -34,11 +34,19 @@
 thread$xcas32:
 	movl	%esi, %eax
 	lock cmpxchgl	%edx, (%rdi)
-	sete	%al
 	ret
 .globl thread$xcas64
 thread$xcas64:
 	movq	%rsi, %rax
 	lock cmpxchgq	%rdx, (%rdi)
-	sete	%al
+	ret
+.globl thread$xchg32
+thread$xchg32:
+	movl	%esi, %eax
+	lock xchgl	(%rdi), %eax
+	ret
+.globl thread$xchg64
+thread$xchg64:
+	movq	%rsi, %rax
+	lock xchgq	(%rdi), %rax
 	ret
--- a/lib/thread/atomic.myr
+++ b/lib/thread/atomic.myr
@@ -6,7 +6,8 @@
 		xset	: (p : @a#, v : @a -> void)
 		xadd	: (p : @a#, v : @a -> @a)
 		xsub	: (p : @a#, v : @a -> @a)
-		xcas	: (p : @a#, old : @a, new : @a -> bool)
+		xcas	: (p : @a#, old : @a, new : @a -> @a)
+		xchg	: (p : @a#, new : @a -> @a)
 	;;
 
 	impl atomic int32
@@ -20,7 +21,8 @@
 	xset	= {p, v; xset32(p castto(uint32#), v castto(uint32))}
 	xadd	= {p, v; -> xadd32(p castto(uint32#), v castto(uint32)) castto(int32)}
 	xsub	= {p, v; -> xsub32(p castto(uint32#), v castto(uint32)) castto(int32)}
-	xcas	= {p, old, new; -> xcas32(p castto(uint32#), old castto(uint32), new castto(uint32))}
+	xcas	= {p, old, new; -> xcas32(p castto(uint32#), old castto(uint32), new castto(uint32)) castto(int32)}
+	xchg	= {p, v; -> xchg32(p castto(uint32#), v castto(uint32)) castto(int32)}
 ;;
 
 
@@ -29,7 +31,8 @@
 	xset	= {p, v; xset64(p castto(uint64#), v castto(uint64))}
 	xadd	= {p, v; -> xadd64(p castto(uint64#), v castto(uint64)) castto(int64)}
 	xsub	= {p, v; -> xsub64(p castto(uint64#), v castto(uint64)) castto(int64)}
-	xcas	= {p, old, new; -> xcas64(p castto(uint64#), old castto(uint64), new castto(uint64))}
+	xcas	= {p, old, new; -> xcas64(p castto(uint64#), old castto(uint64), new castto(uint64)) castto(int64)}
+	xchg	= {p, v; -> xchg64(p castto(uint64#), v castto(uint64)) castto(int64)}
 ;;
 
 impl atomic uint32 =
@@ -38,6 +41,7 @@
 	xadd	= {p, v; -> xadd32(p, v)}
 	xsub	= {p, v; -> xsub32(p, v)}
 	xcas	= {p, old, new; -> xcas32(p, old, new)}
+	xchg	= {p, v; -> xchg32(p, v)}
 ;;
 
 
@@ -47,6 +51,7 @@
 	xadd	= {p, v; -> xadd64(p, v)}
 	xsub	= {p, v; -> xsub64(p, v)}
 	xcas	= {p, old, new; -> xcas64(p, old, new)}
+	xchg	= {p, v; -> xchg64(p, v)}
 ;;
 
 extern const xget32	: (p : uint32# -> uint32)
@@ -61,5 +66,8 @@
 extern const xsub32	: (p : uint32#, v : uint32 -> uint32)
 extern const xsub64	: (p : uint64#, v : uint64 -> uint64)
 
-extern const xcas32	: (p : uint32#, old: uint32, new : uint32 -> bool)
-extern const xcas64	: (p : uint64#, old: uint64, new : uint64 -> bool)
+extern const xcas32	: (p : uint32#, old: uint32, new : uint32 -> uint32)
+extern const xcas64	: (p : uint64#, old: uint64, new : uint64 -> uint64)
+
+extern const xchg32	: (p : uint32#, v : uint32 -> uint32)
+extern const xchg64	: (p : uint64#, v : uint64 -> uint64)
--- a/lib/thread/bld.proj
+++ b/lib/thread/bld.proj
@@ -1,4 +1,5 @@
 lib thread =
+	mutex+linux.myr
 	spawn+linux.myr
 	atomic-impl+x64.s
 	atomic.myr
--- /dev/null
+++ b/lib/thread/mutex+linux.myr
@@ -1,0 +1,79 @@
+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 Sleep = 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, Sleep)
+	;;
+
+	while c != Unlocked
+		sys.futex(&mtx._state, sys.Futexwait | sys.Futexpriv, Sleep, Zptr, Zptr, 0)
+		c = xchg(&mtx._state, 2)
+	;;
+}
+
+const mtxtrylock = {mtx
+	-> xcas(&mtx._state, Unlocked, Locked) == Unlocked
+}
+
+const mtxunlock = {mtx
+	var r
+
+	/* uncontended sleep means we can just unlock and move on */
+	if mtx._state == Sleep
+		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, Sleep) == Sleep
+				->
+			;;
+		;;
+		relax()
+	;;
+
+	r = sys.futex(&mtx._state, sys.Futexwake | sys.Futexpriv, Locked, Zptr, Zptr, 0)
+
+}
+
+const relax = {
+}
--- a/lib/thread/smoketest.myr
+++ b/lib/thread/smoketest.myr
@@ -4,8 +4,9 @@
 
 var val : uint64 = 0
 var done : uint32 = 0
+var mtx : thread.mutex
 
-const setvar = {
+const atomicincvar = {
 	var i
 
 	for i = 0; i < 10_000_000; i++
@@ -15,10 +16,10 @@
 	thread.xset(&done, 1)
 }
 
-const main = {
+const atomictest = {
 	var i
 
-	match thread.spawn(setvar)
+	match thread.spawn(atomicincvar)
 	| `std.Ok tid:
 		for i = 0; i < 100_000; i++
 			thread.xadd(&val, 1)
@@ -26,8 +27,49 @@
 		while thread.xget(&done) == 0
 			/* nothing */
 		;;
-		std.assert(val == 10_100_000, "atomics are broken")
+		std.assert(val == 10_100_000, "atomics are broken\n")
 	| `std.Fail err:
 		std.fatal("errno = {}\n", err)
 	;;
+}
+
+const mtxincvar = {
+	var i
+
+	for i = 0; i < 1_000_000; i++
+		thread.mtxlock(&mtx)
+		val++
+		thread.mtxunlock(&mtx)
+	;;
+	std.write(1, "done\n")
+	thread.xset(&done, 1)
+}
+
+const mtxtest = {
+	var i
+
+	mtx = thread.mkmtx()
+	match thread.spawn(mtxincvar)
+	| `std.Ok tid:
+		for i = 0; i < 1_000_000; i++
+			thread.mtxlock(&mtx)
+			val++
+			thread.mtxunlock(&mtx)
+		;;
+		while thread.xget(&done) == 0
+			/* nothing */
+		;;
+		if val != 2_000_000
+			std.fatal("mutexes are broken, got {}\n", val)
+		;;
+	| `std.Fail err:
+		std.fatal("errno = {}\n", err)
+	;;
+}
+
+const main = {
+	val = 0
+	atomictest()
+	val = 0
+	mtxtest()
 }