shithub: mc

Download patch

ref: 9892228031c0ffcfa95a1443fc565b031525bb3a
parent: e54af86927ced66fb7fa8ad744cfb609390f04d8
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Aug 28 21:29:07 EDT 2018

Implement futures + thread.do

--- a/lib/thread/atomic-impl+plan9-x64.s
+++ b/lib/thread/atomic-impl+plan9-x64.s
@@ -1,3 +1,7 @@
+// get variants
+TEXT thread$xget8+0(SB),1,$0
+	MOVB	(DI), AX
+	RET
 TEXT thread$xget32+0(SB),1,$0
 	MOVL	(DI), AX
 	RET
@@ -8,6 +12,10 @@
 	MOVQ	(DI), AX
 	RET
 
+// set variants
+TEXT thread$xset8+0(SB),1,$0
+	MOVB	SI, (DI)
+	RET
 TEXT thread$xset32+0(SB),1,$0
 	MOVL	SI, (DI)
 	RET
@@ -18,6 +26,11 @@
 	MOVQ	SI, (DI)
 	RET
 
+// add variants
+TEXT thread$xadd8+0(SB),1,$0
+	LOCK; XADDB	SI, (DI)
+	MOVL	SI, AX
+	RET
 TEXT thread$xadd32+0(SB),1,$0
 	LOCK; XADDL	SI, (DI)
 	MOVL	SI, AX
@@ -31,6 +44,11 @@
 	MOVQ	SI, AX
 	RET
 
+// cas variants
+TEXT thread$xcas8+0(SB),1,$0
+	MOVL	SI, AX
+	LOCK; CMPXCHGB	DX, (DI)
+	RET
 TEXT thread$xcas32+0(SB),1,$0
 	MOVL	SI, AX
 	LOCK; CMPXCHGL	DX, (DI)
@@ -44,6 +62,11 @@
 	LOCK; CMPXCHGQ	DX, (DI)
 	RET
 
+// xchg variants
+TEXT thread$xchg8+0(SB),1,$0
+	MOVL	SI, AX
+	LOCK; XCHGB	(DI), AX
+	RET
 TEXT thread$xchg32+0(SB),1,$0
 	MOVL	SI, AX
 	LOCK; XCHGL	(DI), AX
--- a/lib/thread/atomic-impl+x64.s
+++ b/lib/thread/atomic-impl+x64.s
@@ -1,3 +1,10 @@
+# get variants
+.globl thread$xget8
+.globl _thread$xget8
+thread$xget8:
+_thread$xget8:
+	movb	(%rdi), %al
+	ret
 .globl thread$xget32
 .globl _thread$xget32
 thread$xget32:
@@ -15,6 +22,13 @@
 	movq	(%rdi), %rax
 	ret
 
+# set variants
+.globl thread$xset8
+.globl _thread$xset8
+thread$xset8:
+_thread$xset8:
+	movl	%esi, (%rdi)
+	ret
 .globl thread$xset32
 .globl _thread$xset32
 thread$xset32:
@@ -32,6 +46,14 @@
 	movq	%rsi, (%rdi)
 	ret
 
+# add variants
+.globl thread$xadd8
+.globl _thread$xadd8
+thread$xadd8:
+_thread$xadd8:
+	lock xaddb	%sil, (%rdi)
+	movb %sil,%al
+	ret
 .globl thread$xadd32
 .globl _thread$xadd32
 thread$xadd32:
@@ -51,6 +73,14 @@
 	movq %rsi,%rax
 	ret
 
+# cas variants 
+.globl thread$xcas8
+.globl _thread$xcas8
+thread$xcas8:
+_thread$xcas8:
+	movb	%sil, %al
+	lock cmpxchgb	%dl, (%rdi)
+	ret
 .globl thread$xcas32
 .globl _thread$xcas32
 thread$xcas32:
@@ -66,10 +96,18 @@
 thread$xcasp:
 _thread$xcas64:
 _thread$xcasp:
-	movq	%rsi, %rax
+	movq		%rsi, %rax
 	lock cmpxchgq	%rdx, (%rdi)
 	ret
 
+# xchg variants
+.globl thread$xchg8
+.globl _thread$xchg8
+thread$xchg8:
+_thread$xchg8:
+	movb		%sil, %al
+	lock xchgb	(%rdi), %al
+	ret
 .globl thread$xchg32
 .globl _thread$xchg32
 thread$xchg32:
--- a/lib/thread/atomic.myr
+++ b/lib/thread/atomic.myr
@@ -10,6 +10,7 @@
 		xchg	: (p : @a#, new : @a -> @a)
 	;;
 
+	impl atomic bool
 	impl atomic int32
 	impl atomic int64
 	impl atomic uint32
@@ -20,25 +21,38 @@
 	generic xcasptr : (p : @a##, old : std.option(@a#), new : std.option(@a#) -> std.option(@a#))
 	generic xchgptr : (p : @a##, new : std.option(@a#) -> std.option(@a#))
 
+	pkglocal extern const xget8	: (p : uint8# -> uint8)
 	pkglocal extern const xget32	: (p : uint32# -> uint32)
 	pkglocal extern const xget64	: (p : uint64# -> uint64)
 	pkglocal extern const xgetp	: (p : std.intptr# -> std.intptr)
 
+	pkglocal extern const xset8	: (p : uint8#, v : uint8 -> void)
 	pkglocal extern const xset32	: (p : uint32#, v : uint32 -> void)
 	pkglocal extern const xset64	: (p : uint64#, v : uint64 -> void)
 	pkglocal extern const xsetp	: (p : std.intptr#, v : std.intptr -> void)
 
+	pkglocal extern const xadd8	: (p : uint8#, v : uint8 -> uint8)
 	pkglocal extern const xadd32	: (p : uint32#, v : uint32 -> uint32)
 	pkglocal extern const xadd64	: (p : uint64#, v : uint64 -> uint64)
 	pkglocal extern const xaddp	: (p : std.intptr#, v : std.intptr -> std.intptr)
 
+	pkglocal extern const xcas8	: (p : uint8#, old: uint8, new : uint8 -> uint8)
 	pkglocal extern const xcas32	: (p : uint32#, old: uint32, new : uint32 -> uint32)
 	pkglocal extern const xcas64	: (p : uint64#, old: uint64, new : uint64 -> uint64)
 	pkglocal extern const xcasp	: (p : std.intptr#, old: std.intptr, new : std.intptr -> std.intptr)
 
+	pkglocal extern const xchg8	: (p : uint8#, v : uint8 -> uint8)
 	pkglocal extern const xchg32	: (p : uint32#, v : uint32 -> uint32)
 	pkglocal extern const xchg64	: (p : uint64#, v : uint64 -> uint64)
 	pkglocal extern const xchgp	: (p : std.intptr#, v : std.intptr -> std.intptr)
+;;
+
+impl atomic bool =
+	xget	= {p; -> (xget8((p : uint8#)) : bool)}
+	xset	= {p, v; xset8((p : uint8#), (v : uint8))}
+	xadd	= {p, v; -> (xadd8((p : uint8#), (v : uint8)) : bool)}
+	xcas	= {p, old, new; -> (xcas8((p : uint8#), (old : uint8), (new : uint8)) : bool)}
+	xchg	= {p, v; -> (xchg8((p : uint8#), (v : uint8)) : bool)}
 ;;
 
 impl atomic int32 =
--- a/lib/thread/bld.sub
+++ b/lib/thread/bld.sub
@@ -2,6 +2,10 @@
 	common.myr
 	hookstd.myr	# install thread hooks
 
+	# higher level apis
+	future.myr
+	do.myr
+
 	# generic fallbacks
 	condvar.myr
 	mutex.myr
--- /dev/null
+++ b/lib/thread/do.myr
@@ -1,0 +1,17 @@
+use std
+
+use "future"
+use "spawn"
+
+pkg thread =
+	generic do	: (fn : (-> @a) -> future(@a)#)
+;;
+
+generic do = {fn
+	var r
+
+	r = mkfut()
+	spawn({; futput(r, fn()) })
+	-> r
+}
+
--- a/lib/thread/future.myr
+++ b/lib/thread/future.myr
@@ -1,63 +1,55 @@
 use std
 
 use "mutex"
+use "condvar"
+use "atomic"
 
 pkg thread =
 	type future(@a) = struct
 		mtx	: mutex
+		cv	: cond
 		set	: bool
 		val	: @a
 	;;
 
-	generic mkfut	: (-> future(@a))
-	generic futset	: (fut : future(@a)#, val : @a -> bool)
+	generic mkfut	: (-> future(@a)#)
+	generic futput	: (fut : future(@a)#, val : @a -> void)
 	generic futget	: (fut : future(@a)# -> @a)
-	generic futtryget	: (fut : future(@a)# -> std.option(@a))
-	generic futclear	: (fut : future(@a)# -> void)
+	generic futpeek	: (fut : future(@a)# -> @a)
 ;;
 
-const Unset = 0
-const Waiting = 1
-const Set = 2
-
 generic mkfut = {
 	var fut
 
-	fut = [.mtx = mkmtx() ]
-	mtxlock(&fut.mtx)
+	fut = std.alloc()
+	fut.mtx = mkmtx()
+	fut.cv = mkcond(&fut.mtx)
+	fut.set = false
 	-> fut
 }
 
-generic futset = {fut, val
-	if fut.set
-		-> false
-	;;
-	/* compiler doesn't reorder shit */
+generic futput = {fut, val
 	fut.val = val
-	fut.set = true
-	mtxunlock(&fut.mtx)
-	-> true
+	xset(&fut.set, true)
+	condsignal(&fut.cv)
 }
 
-generic futtryget = {fut
-	var val
-
-	if !fut.set
-		-> `std.None
-	;;
-	mtxlock(&fut.mtx)
-	val = fut.val
-	mtxunlock(&fut.mtx)
-	-> `std.Some val
-}
-
 generic futget = {fut
 	var val
-
-	mtxlock(&fut.mtx)
-	val = fut.val
-	mtxunlock(&fut.mtx)
+	
+	val = futpeek(fut)
+	std.free(fut)
 	-> val
 }
 
+generic futpeek = {fut
+	if !xget(&fut.set)
+		mtxlock(&fut.mtx)
+		if !xget(&fut.set)
+			condwait(&fut.cv)
+		;;
+		mtxunlock(&fut.mtx)
+	;;
+	-> fut.val
+}
 
--- /dev/null
+++ b/lib/thread/test/do.myr
@@ -1,0 +1,30 @@
+use std
+use thread
+
+const main = {
+	match std.espork(["echo", "hello"][:])
+	| `std.Err e:
+		std.fatal("could not spork\n")
+	| `std.Ok (pid, in, out, err):
+		std.close(in)
+		var w = thread.do({;-> std.wait(pid)})
+		var o = thread.do({;-> std.fslurp(out)})
+		var e = thread.do({;-> std.fslurp(err)})
+
+		match thread.futget(w)
+		| `std.Wsuccess:	/* ok */
+		| bad:	std.fatal("bad wait: {}\n", bad)
+		;;
+
+		match thread.futget(o)
+		| `std.Ok "hello\n":	/* ok */
+		| bad:	std.fatal("bad out: {}\n", bad)
+		;;
+
+		match thread.futget(e)
+		| `std.Ok "":	/* ok */
+		| bad:	std.fatal("bad err: {}\n", bad)
+		;;
+	;;
+}
+
--- a/lib/thread/test/future.myr
+++ b/lib/thread/test/future.myr
@@ -2,49 +2,18 @@
 use sys
 use thread
 
-use "util"
-
-var fut
-var nready : int32
-var ndone : int32
-
 const main = {
-	nready = 0
-	ndone = 0
-	fut = thread.mkfut()
-	/* set after we have some waiters */
-	mkherd(100, getfuture)
-	while nready != 100
-		/* spin */
-	;;
-	std.put("done waiting for ready\n")
-	std.assert(ndone == 0, "thread proceeded too soon\n")
-	thread.futset(&fut, 666)
-	std.assert(thread.futset(&fut, 1) == false, "double set future\n")
-	while ndone != 100
-		/* spin */
-	;;
-	std.put("double set future ok")
-	/* start up a few more to make sure we can still read */
-	mkherd(50, getfuture)
-	while ndone != 150
-		/* spin */
-	;;
+	var f
 
-	
-	/* set ahead of time */
-	ndone = 0
-	fut = thread.mkfut()
-	thread.futset(&fut, 666)
-	std.assert(thread.futset(&fut, 666) == false, "double set future\n")
-	mkherd(100, getfuture)
-	while ndone != 100
-		/* spin */
-	;;
-}
+	f = thread.mkfut()
+	thread.futput(f, 123)
+	std.assert(thread.futget(f) == 123, "the future is broken\n")
 
-const getfuture = {
-	thread.xadd(&nready, 1)
-	std.assert(thread.futget(&fut) == 666, "wrong value gotten from future")
-	thread.xadd(&ndone, 1)
+	f = thread.mkfut()
+	thread.spawn({
+		std.usleep(10_000)
+		thread.futput(f, 321)
+	})
+	std.assert(thread.futget(f) == 321, "the future is broken\n")
 }
+