ref: 10a209f1937d161e1ff5409deb06253facff73a6
author: Ori Bernstein <ori@eigenstate.org>
date: Thu Aug 27 19:53:41 EDT 2015
Start of libthread for linux.
--- /dev/null
+++ b/lib/thread/atomic-impl+x64.s
@@ -1,0 +1,44 @@
+.globl thread$xget32
+thread$xget32:
+ movl (%rdi), %eax
+ ret
+.globl thread$xget64
+thread$xget64:
+ movq (%rdi), %rax
+ ret
+.globl thread$xset32
+thread$xset32:
+ movl %esi, (%rdi)
+ ret
+.globl thread$xset64
+thread$xset64:
+ movq %rsi, (%rdi)
+ ret
+.globl thread$xadd32
+thread$xadd32:
+ lock xaddl %esi, (%rdi)
+ ret
+.globl thread$xadd64
+thread$xadd64:
+ lock xaddq %rsi, (%rdi)
+ ret
+.globl thread$xsub32
+thread$xsub32:
+ lock xaddl %esi, (%rdi)
+ ret
+.globl thread$xsub64
+thread$xsub64:
+ lock xaddq %rsi, (%rdi)
+ ret
+.globl thread$xcas32
+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
--- /dev/null
+++ b/lib/thread/atomic.myr
@@ -1,0 +1,65 @@
+use std
+
+pkg thread =
+ trait atomic @a::(integral,numeric) =
+ xget : (p : @a# -> @a)
+ 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)
+ ;;
+
+ impl atomic int32
+ impl atomic int64
+ impl atomic uint32
+ impl atomic uint64
+;;
+
+impl atomic int32 =
+ xget = {p; -> xget32(p castto(uint32#)) castto(int32)}
+ 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))}
+;;
+
+
+impl atomic int64 =
+ xget = {p; -> xget64(p castto(uint64#)) castto(int64)}
+ 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))}
+;;
+
+impl atomic uint32 =
+ xget = {p; -> xget32(p)}
+ xset = {p, v; xset32(p, v)}
+ xadd = {p, v; -> xadd32(p, v)}
+ xsub = {p, v; -> xsub32(p, v)}
+ xcas = {p, old, new; -> xcas32(p, old, new)}
+;;
+
+
+impl atomic uint64 =
+ xget = {p; -> xget64(p)}
+ xset = {p, v; xset64(p, v)}
+ xadd = {p, v; -> xadd64(p, v)}
+ xsub = {p, v; -> xsub64(p, v)}
+ xcas = {p, old, new; -> xcas64(p, old, new)}
+;;
+
+extern const xget32 : (p : uint32# -> uint32)
+extern const xget64 : (p : uint64# -> uint64)
+
+extern const xset32 : (p : uint32#, v : uint32 -> void)
+extern const xset64 : (p : uint64#, v : uint64 -> void)
+
+extern const xadd32 : (p : uint32#, v : uint32 -> uint32)
+extern const xadd64 : (p : uint64#, v : uint64 -> uint64)
+
+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)
--- /dev/null
+++ b/lib/thread/bld.proj
@@ -1,0 +1,10 @@
+lib thread =
+ spawn+linux.myr
+ clone+linux-x64.s
+ atomic-impl+x64.s
+ atomic.myr
+;;
+
+test smoketest =
+ smoketest.myr
+;;
--- /dev/null
+++ b/lib/thread/clone+linux-x64.s
@@ -1,0 +1,27 @@
+.globl thread$clone
+thread$clone:
+ pushq %r15
+ movq 16(%rsp),%r15
+ /* clone(flags, stack, ptid, tls, ctid, regs) */
+ movq $56,%rax /* syscall num */
+ /* %rdi: flags */
+ /* %rsi: stack */
+ /* %rdx: ptid */
+ movq %rcx,%r10 /* tls */
+ /* %r8: ctid */
+ /* %r9: regs */
+ syscall
+
+ /* fn() */
+ testl %eax,%eax
+ jnz parent
+ call *%r15
+
+ /* exit(0) */
+ movq $60, %rax /* exit */
+ movq $0, %rdi /* arg: 0 */
+ syscall
+
+parent:
+ popq %r15
+ ret
--- /dev/null
+++ b/lib/thread/smoketest.myr
@@ -1,0 +1,33 @@
+use std
+use sys
+use thread
+
+var val : uint64 = 0
+var done : uint32 = 0
+
+const setvar = {
+ var i
+
+ for i = 0; i < 10_000_000; i++
+ thread.xadd(&val, 1)
+ ;;
+ std.write(1, "done\n")
+ thread.xset(&done, 1)
+}
+
+const main = {
+ var i
+
+ match thread.spawn(setvar)
+ | `std.Ok tid:
+ for i = 0; i < 100_000; i++
+ thread.xadd(&val, 1)
+ ;;
+ while thread.xget(&done) == 0
+ /* nothing */
+ ;;
+ std.assert(val == 10_100_000, "atomics are broken")
+ | `std.Fail err:
+ std.fatal("errno = {}\n", err)
+ ;;
+}
--- /dev/null
+++ b/lib/thread/spawn+linux.myr
@@ -1,0 +1,56 @@
+use sys
+use std
+
+pkg thread =
+ type tid = sys.pid
+
+ const spawn : (fn : (-> void) -> std.result(tid, byte[:]))
+;;
+
+extern const clone : ( flags : sys.cloneopt, \
+ stk : byte#, \
+ ptid : sys.pid#, \
+ tls : byte#, \
+ ctid : sys.pid#, \
+ ptreg : byte#, \
+ fn : (-> void) \
+ -> sys.pid)
+/* Holy shit flag mania. */
+const Thrflag = sys.Clonevm | sys.Clonefs | sys.Clonefiles | \
+ sys.Clonesighand | sys.Clonethread |sys.Clonesysvsem | \
+ sys.Clonesettls | sys.Cloneparentsettid | sys.Clonechildcleartid
+
+const Stacksz = 8*std.MiB
+
+const spawn = {fn
+ -> spawnstk(fn, Stacksz)
+}
+
+const spawnstk = {fn, sz
+ var stk, tid, ctid, ret
+
+ stk = getstk(sz)
+ if stk == sys.Mapbad
+ -> `std.Fail "couldn't get stack"
+ ;;
+
+ ret = clone(Thrflag, stk, &tid, 0 castto(byte#), &ctid, 0 castto(byte#), fn) castto(tid)
+ if ret < 0
+ std.put("errno={}\n", -ret)
+ -> `std.Fail "couldn't spawn thread"
+ ;;
+ -> `std.Ok ret
+}
+
+const getstk = {sz
+ var p, m
+
+ p = sys.mmap(0 castto(byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
+ if p == sys.Mapbad
+ -> p
+ ;;
+ /* stack starts at the top of memory and grows down. */
+ m = p castto(std.intptr)
+ m += sz castto(std.intptr)
+ -> m castto(byte#)
+}