summaryrefslogtreecommitdiff
path: root/lib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'lib/thread')
-rw-r--r--lib/thread/bld.sub12
-rw-r--r--lib/thread/common.myr4
-rw-r--r--lib/thread/exit+freebsd-x64.s11
-rw-r--r--lib/thread/exit+linux-x64.s13
-rw-r--r--lib/thread/exit+openbsd-x64.s11
-rw-r--r--lib/thread/fsbase+freebsd.myr28
-rw-r--r--lib/thread/fsbase+linux.myr28
-rw-r--r--lib/thread/fsbase+netbsd.myr28
-rw-r--r--lib/thread/fsbase+openbsd.myr16
-rw-r--r--lib/thread/mutex+futex.myr43
-rw-r--r--lib/thread/mutex.myr1
-rw-r--r--lib/thread/rwlock+futex.myr27
-rw-r--r--lib/thread/spawn+freebsd.myr78
-rw-r--r--lib/thread/spawn+linux.myr63
-rw-r--r--lib/thread/spawn+openbsd.myr60
-rw-r--r--lib/thread/spawn+osx.myr68
-rw-r--r--lib/thread/start+osx-x64.s11
-rw-r--r--lib/thread/test/die.myr8
-rw-r--r--lib/thread/test/tls.myr49
-rw-r--r--lib/thread/tls+fsbase.myr59
-rw-r--r--lib/thread/tls+osx.myr70
-rw-r--r--lib/thread/tls-impl+fsbase-x64.s48
-rw-r--r--lib/thread/tls-impl+osx-x64.s64
-rw-r--r--lib/thread/types+fsbase.myr19
-rw-r--r--lib/thread/types+osx.myr20
25 files changed, 659 insertions, 180 deletions
diff --git a/lib/thread/bld.sub b/lib/thread/bld.sub
index c380091..57aed13 100644
--- a/lib/thread/bld.sub
+++ b/lib/thread/bld.sub
@@ -14,6 +14,11 @@ lib thread =
sem.myr
waitgrp.myr
+ # fsbase-based impls
+ tls+fsbase.myr
+ tls-impl+fsbase-x64.s
+ types+fsbase.myr
+
# futex-based impls
mutex+futex.myr
rwlock+futex.myr
@@ -23,6 +28,7 @@ lib thread =
# linux impl of basic thread primitives
condvar+linux.myr
exit+linux-x64.s
+ fsbase+linux.myr
futex+linux.myr
ncpu+linux.myr
spawn+linux.myr
@@ -30,6 +36,7 @@ lib thread =
# freebsd impl of thread primitives
condvar+freebsd.myr
exit+freebsd-x64.s
+ fsbase+freebsd.myr
futex+freebsd.myr
ncpu+freebsd.myr
spawn+freebsd.myr
@@ -37,6 +44,7 @@ lib thread =
# netbsd impl of thread primitives
#condvar+netbsd.myr
#mutex+netbsd.myr
+ fsbase+netbsd.myr
spawn+netbsd.myr
#ncpu+netbsd.myr
#exit+netbsd-x64.s
@@ -46,6 +54,9 @@ lib thread =
futex+osx.myr
spawn+osx.myr
start+osx-x64.s
+ tls+osx.myr
+ tls-impl+osx-x64.s
+ types+osx.myr
# 9front impl of thread primitives
#condvar+plan9.myr
@@ -58,6 +69,7 @@ lib thread =
# openbsd impl of thread primitives
condvar+openbsd:6.2.myr
exit+openbsd-x64.s
+ fsbase+openbsd.myr
futex+openbsd:6.2.myr
ncpu+openbsd.myr
spawn+openbsd.myr
diff --git a/lib/thread/common.myr b/lib/thread/common.myr
index 66fc9d5..3e4f1f5 100644
--- a/lib/thread/common.myr
+++ b/lib/thread/common.myr
@@ -1,5 +1,3 @@
-use std
-
-pkg thread =
+pkg thread =
pkglocal generic Zptr : @a# = (0 : @a#)
;;
diff --git a/lib/thread/exit+freebsd-x64.s b/lib/thread/exit+freebsd-x64.s
index d8952b6..45a4bb7 100644
--- a/lib/thread/exit+freebsd-x64.s
+++ b/lib/thread/exit+freebsd-x64.s
@@ -1,19 +1,12 @@
/*
const thread.exit : (stacksz : std.size -> void)
-NOTE: must be called from the bottom of the stack, since
-we assume that %rbp is in the top 4k of the stack.
*/
.globl thread$exit
thread$exit:
- /* find top of stack */
- movq %rbp,%rdi /* addr */
- andq $~0xfff,%rdi /* align it */
- addq $0x1000,%rdi
-
/* munmap(base, size) */
movq $73,%rax /* munmap */
- movq -8(%rdi),%rsi /* size */
- subq %rsi,%rdi /* move to base ptr */
+ movq %fs:0x08,%rdi /* base */
+ movq %fs:0x10,%rsi /* stksz */
syscall
/* thr_exit(null) */
diff --git a/lib/thread/exit+linux-x64.s b/lib/thread/exit+linux-x64.s
index a54e802..d736f6d 100644
--- a/lib/thread/exit+linux-x64.s
+++ b/lib/thread/exit+linux-x64.s
@@ -1,19 +1,12 @@
/*
-const thread.exit : (stacksz : std.size -> void)
-NOTE: must be called from the bottom of the stack, since
-we assume that %rbp is in the top 4k of the stack.
+const thread.exit : (-> void)
*/
.globl thread$exit
thread$exit:
- /* find top of stack */
- movq %rbp,%rdi /* addr */
- andq $~0xfff,%rdi /* align it */
- addq $0x1000,%rdi
-
/* munmap(base, size) */
movq $11,%rax /* munmap */
- movq -8(%rdi),%rsi /* size */
- subq %rsi,%rdi /* move to base ptr */
+ movq %fs:0x08,%rdi /* base */
+ movq %fs:0x10,%rsi /* stksz */
syscall
/* thread_exit(0) */
diff --git a/lib/thread/exit+openbsd-x64.s b/lib/thread/exit+openbsd-x64.s
index 6421cc3..0eaf63a 100644
--- a/lib/thread/exit+openbsd-x64.s
+++ b/lib/thread/exit+openbsd-x64.s
@@ -1,15 +1,8 @@
/*
const thread.exit : (stacksz : std.size -> void)
-NOTE: must be called from the bottom of the stack, since
-we assume that %rbp is in the top 4k of the stack.
*/
.globl thread$exit
thread$exit:
- /* find top of stack */
- movq %rbp,%rdi /* addr */
- andq $~0xfff,%rdi /* align it */
- addq $0x1000,%rdi
-
/*
Because OpenBSD wants a valid stack whenever
we enter the kernel, we need to toss a preallocated
@@ -19,8 +12,8 @@ thread$exit:
/* munmap(base, size) */
movq $73,%rax /* munmap */
- movq -8(%rdi),%rsi /* size */
- subq %rsi,%rdi /* move to base ptr */
+ movq %fs:0x08,%rdi /* base */
+ movq %fs:0x10,%rsi /* stksz */
syscall
/* __threxit(0) */
diff --git a/lib/thread/fsbase+freebsd.myr b/lib/thread/fsbase+freebsd.myr
new file mode 100644
index 0000000..e648aba
--- /dev/null
+++ b/lib/thread/fsbase+freebsd.myr
@@ -0,0 +1,28 @@
+use std
+use sys
+
+use "types"
+
+pkg thread =
+ pkglocal const setfsbase : (h : tlshdr# -> void)
+ pkglocal const getfsbase : (-> tlshdr#)
+;;
+
+const setfsbase = {h
+ match sys.sysarch(sys.Archamd64setfs, &(h : void#))
+ | 0:
+ | err:
+ std.fput(std.Err, "error: sysarch returned {}\n", err)
+ std.suicide()
+ ;;
+}
+
+const getfsbase = {
+ var h
+ match sys.sysarch(sys.Archamd64getfs, &h)
+ | 0: -> (h : tlshdr#)
+ | err:
+ std.fput(std.Err, "error: sysarch returned {}\n", err)
+ std.suicide()
+ ;;
+}
diff --git a/lib/thread/fsbase+linux.myr b/lib/thread/fsbase+linux.myr
new file mode 100644
index 0000000..1641a64
--- /dev/null
+++ b/lib/thread/fsbase+linux.myr
@@ -0,0 +1,28 @@
+use std
+use sys
+
+use "types"
+
+pkg thread =
+ pkglocal const setfsbase : (h : tlshdr# -> void)
+ pkglocal const getfsbase : (-> tlshdr#)
+;;
+
+const setfsbase = {h
+ match sys.arch_prctl(sys.Archsetfs, (h : void#))
+ | 0:
+ | err:
+ std.fput(std.Err, "error: arch_prctl returned {}\n", err)
+ std.suicide()
+ ;;
+}
+
+const getfsbase = {
+ var h : tlshdr#
+ match sys.arch_prctl(sys.Archgetfs, (&h : void#))
+ | 0: -> h
+ | err:
+ std.fput(std.Err, "error: arch_prctl returned {}\n", err)
+ std.suicide()
+ ;;
+}
diff --git a/lib/thread/fsbase+netbsd.myr b/lib/thread/fsbase+netbsd.myr
new file mode 100644
index 0000000..3c470cf
--- /dev/null
+++ b/lib/thread/fsbase+netbsd.myr
@@ -0,0 +1,28 @@
+use std
+use sys
+
+use "types"
+
+pkg thread =
+ pkglocal const setfsbase : (h : tlshdr# -> void)
+ pkglocal const getfsbase : (-> tlshdr#)
+;;
+
+const setfsbase = {h
+ match sys.sysarch(sys.X8664setfsbase, &(h : void#))
+ | 0:
+ | err:
+ std.fput(std.Err, "error: sysarch returned: {}\n", err)
+ std.suicide()
+ ;;
+}
+
+const getfsbase = {
+ var h
+ match sys.sysarch(sys.X8664getfsbase, &h)
+ | 0: -> (h : tlshdr#)
+ | err:
+ std.fput(std.Err, "error: sysarch returned: {}\n", err)
+ std.suicide()
+ ;;
+}
diff --git a/lib/thread/fsbase+openbsd.myr b/lib/thread/fsbase+openbsd.myr
new file mode 100644
index 0000000..7ae92b2
--- /dev/null
+++ b/lib/thread/fsbase+openbsd.myr
@@ -0,0 +1,16 @@
+use sys
+
+use "types"
+
+pkg thread =
+ pkglocal const setfsbase : (h : tlshdr# -> void)
+ pkglocal const getfsbase : (-> tlshdr#)
+;;
+
+const setfsbase = {h
+ sys.__set_tcb((h : void#))
+}
+
+const getfsbase = {
+ -> (sys.__get_tcb() : tlshdr#)
+}
diff --git a/lib/thread/mutex+futex.myr b/lib/thread/mutex+futex.myr
index 50e8406..bb9012d 100644
--- a/lib/thread/mutex+futex.myr
+++ b/lib/thread/mutex+futex.myr
@@ -1,9 +1,14 @@
+use std
+
use "atomic"
use "futex"
+use "tls"
+use "types"
pkg thread =
type mutex = struct
_state : ftxtag
+ _owner : tid
;;
const mkmtx : (-> mutex)
@@ -21,12 +26,19 @@ const Contended = 2
var nspin = 10 /* FIXME: pick a sane number, based on CPU count */
const mkmtx = {
- -> [._state = Unlocked]
+ -> [._state = Unlocked, ._owner = -1]
}
const mtxlock = {mtx
var c
+ if mtx._owner == tid()
+ std.fput(std.Err,
+ "error: thread {} attempted to relock a mutex it already holds\n",
+ tid())
+ std.suicide()
+ ;;
+
/*
Uncontended case: we get an unlocked mutex, and we lock it.
*/
@@ -34,6 +46,7 @@ const mtxlock = {mtx
for var i = 0; i < nspin; i++
c = xcas(&mtx._state, Unlocked, Locked)
if c == Unlocked
+ mtx._owner = tid()
-> void
;;
;;
@@ -51,14 +64,32 @@ const mtxlock = {mtx
ftxwait(&mtx._state, Contended, -1)
c = xchg(&mtx._state, Contended)
;;
+ mtx._owner = tid()
}
const mtxtrylock = {mtx
- -> xcas(&mtx._state, Unlocked, Locked) == Unlocked
+ if xcas(&mtx._state, Unlocked, Locked) == Unlocked
+ mtx._owner = tid()
+ -> true
+ ;;
+ -> false
}
const mtxunlock = {mtx
/*
+ Nonatomically loading mtx._owner may produce false negatives on
+ weakly-ordered architectures but having to atomically store and load
+ mtx._owner doesn't seem worth it.
+ */
+ if mtx._owner != tid()
+ std.fput(std.Err,
+ "error: thread {} attempted to unlock a mutex last held by {}\n",
+ tid(), mtx._owner)
+ std.suicide()
+ ;;
+ mtx._owner = -1
+
+ /*
Either the lock is contended or it's uncontended. Any other
state is a bug.
@@ -72,7 +103,15 @@ const mtxunlock = {mtx
}
const mtxcontended = {mtx
+ if mtx._owner == tid()
+ std.fput(std.Err,
+ "error: thread {} attempted to relock a mutex it already holds\n",
+ tid())
+ std.suicide()
+ ;;
+
while xchg(&mtx._state, Contended) != Unlocked
ftxwait(&mtx._state, Contended, -1)
;;
+ mtx._owner = tid()
}
diff --git a/lib/thread/mutex.myr b/lib/thread/mutex.myr
index b37f2fb..100ab45 100644
--- a/lib/thread/mutex.myr
+++ b/lib/thread/mutex.myr
@@ -1,5 +1,4 @@
use std
-use sys
use "atomic"
diff --git a/lib/thread/rwlock+futex.myr b/lib/thread/rwlock+futex.myr
index 4975c95..a117440 100644
--- a/lib/thread/rwlock+futex.myr
+++ b/lib/thread/rwlock+futex.myr
@@ -2,6 +2,8 @@ use std
use "atomic"
use "futex"
+use "tls"
+use "types"
pkg thread =
/*
@@ -13,6 +15,7 @@ pkg thread =
*/
type rwlock = struct
_state : ftxtag
+ _owner : tid
;;
const mkrwlock : (-> rwlock)
@@ -28,7 +31,7 @@ const Nrmask = 0x7fffffff
const Waitbit = 0x80000000
const mkrwlock = {
- -> [._state = 0]
+ -> [._state = 0, ._owner = -1]
}
const rdlock = {rw
@@ -61,6 +64,13 @@ const rdlock = {rw
const wrlock = {rw
for ; ;
+ if rw._owner == tid()
+ std.fput(std.Err,
+ "error: thread {} attempted to relock an rwlock it already holds\n",
+ tid())
+ std.suicide()
+ ;;
+
/*
`_state` must be 0 for a writer to acquire the lock. Anything
else means the lock is either held or in the process of being
@@ -68,6 +78,7 @@ const wrlock = {rw
*/
var s = xcas(&rw._state, 0, Nrmask)
if s == 0
+ rw._owner = tid()
-> void
;;
@@ -98,7 +109,11 @@ const tryrdlock = {rw
}
const trywrlock = {rw
- -> xcas(&rw._state, 0, Nrmask) == 0
+ if xcas(&rw._state, 0, Nrmask) == 0
+ rw._owner = tid()
+ -> true
+ ;;
+ -> false
}
const rdunlock = {rw
@@ -122,6 +137,14 @@ const rdunlock = {rw
}
const wrunlock = {rw
+ if rw._owner != tid()
+ std.fput(std.Err,
+ "error: thread {} attempted to unlock an rwlock last held by {}\n",
+ tid(), rw._owner)
+ std.suicide()
+ ;;
+ rw._owner = -1
+
/*
If the wait bit was set then there are one or more waiting readers,
writers, or both. In the first and third cases, we need to wake
diff --git a/lib/thread/spawn+freebsd.myr b/lib/thread/spawn+freebsd.myr
index cdc7673..66a28bd 100644
--- a/lib/thread/spawn+freebsd.myr
+++ b/lib/thread/spawn+freebsd.myr
@@ -1,9 +1,12 @@
use sys
use std
-pkg thread =
- type tid = uint64
+use "common"
+use "fsbase"
+use "tls"
+use "types"
+pkg thread =
const spawn : (fn : (-> void) -> std.result(tid, byte[:]))
;;
@@ -16,60 +19,63 @@ const spawn = {fn
}
const spawnstk = {fn, sz
- var stk : byte#, tid, ctid, ret
- var szp, f, tos, env, envsz
+ var stk, tos, stksz, hdr, tid = -1, ret
- stk = getstk(sz)
+ stk = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
if stk == sys.Mapbad
-> `std.Err "couldn't get stack"
;;
- tid = -1
- /* find top of stack */
- tos = (stk : std.intptr) + (sz : std.intptr)
-
- /* store the stack size */
- tos -= sizeof(sys.size)
- sz -= sizeof(sys.size)
- szp = (tos : sys.size#)
- szp# = Stacksz
-
- /* store the function we call */
- envsz = std.fnenvsz(fn)
- tos -= (envsz : std.intptr)
- sz -= (envsz : sys.size)
- env = tos
- tos -= sizeof((->void))
- sz -= sizeof((->void))
- f = (tos : (->void)#)
- f# = std.fnbdup(fn, (env : byte#)[:envsz])
- var repr = (&fn : int64[2]#)#
+ (tos, stksz, hdr) = initstk(stk, fn, sz)
ret = sys.thr_new(&[
.startfn = (startthread : void#),
.arg = (tos : void#),
.stkbase = (stk : byte#),
- .stksz = sz,
- .tid = &ctid,
+ .stksz = stksz,
+ .tid = (&hdr.tid : uint64#),
.ptid = &tid,
.flags = 2,
- .rtp = (0 : sys.rtprio#)
+ .rtp = Zptr,
], sizeof(sys.thrparam))
if ret < 0
+ sys.munmap(stk, sz)
-> `std.Err "couldn't spawn thread"
;;
-> `std.Ok (tid : tid)
}
-const getstk = {sz
- var p, m
+const initstk = {stk, fn, sz
+ var stksz, len, tos, hdr, fp, env, envsz
- p = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
- if p == sys.Mapbad
- -> p
- ;;
- m = (p : std.intptr)
- -> (m : byte#)
+ stksz = sz
+ len = tlslen()
+ stksz -= (sizeof(tlshdr) + ((len : sys.size) * sizeof(void#)) + 0xf) & ~0xf
+ tos = (stk : std.intptr) + (stksz : std.intptr)
+ hdr = (tos : tlshdr#)
+ hdr.base = stk
+ hdr.stksz = sz
+
+ var fn1 = {
+ /*
+ We write `hdr.len` here because it follows `hdr.tid` so it gets
+ overwritten by the kernel in `thr_new`. Even though `sys.pid`
+ is 32 bits, `thr_param.tid` is a `uint64#` for legacy reasons.
+ */
+ hdr.len = len
+ setfsbase(hdr)
+ fn()
+ }
+
+ envsz = std.fnenvsz(fn1)
+ tos -= (envsz : std.intptr)
+ stksz -= (envsz : sys.size)
+ env = tos
+ tos -= sizeof((->void))
+ stksz -= sizeof((->void))
+ fp = (tos : (->void)#)
+ fp# = std.fnbdup(fn1, (env : byte#)[:envsz])
+ -> ((tos : byte#), stksz, hdr)
}
const startthread = {f : (-> void)#
diff --git a/lib/thread/spawn+linux.myr b/lib/thread/spawn+linux.myr
index a56317f..d56ae47 100644
--- a/lib/thread/spawn+linux.myr
+++ b/lib/thread/spawn+linux.myr
@@ -1,72 +1,67 @@
use sys
use std
-pkg thread =
- type tid = sys.pid
+use "common"
+use "tls"
+use "types"
+pkg thread =
const spawn : (fn : (-> void) -> std.result(tid, byte[:]))
;;
+const Stacksz = 8*std.MiB
extern const exit : (-> void)
/* 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 Thrflag = sys.Clonevm | sys.Clonefs | sys.Clonefiles | \
+ sys.Clonesighand | sys.Clonethread | sys.Clonesettls | \
+ sys.Clonechildsettid
const spawn = {fn
-> spawnstk(fn, Stacksz)
}
const spawnstk = {fn, sz
- var stk : byte#, tid, ctid, ret
+ var stk, tos, hdr, ret
- stk = getstk(sz)
+ stk = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
if stk == sys.Mapbad
-> `std.Err "couldn't get stack"
;;
- stk = initstack(stk, fn, Stacksz)
+ (tos, hdr) = initstk(stk, fn, sz)
- ret = sys.fnclone(Thrflag, \
- (stk : byte#),\
- &tid, (0 : byte#), \
- &ctid, (0 : byte#), \
+ ret = sys.fnclone(Thrflag,
+ tos,
+ Zptr,
+ (hdr : byte#),
+ (&hdr.tid : sys.pid#),
+ Zptr,
(startthread : void#))
if ret < 0
+ sys.munmap(stk, sz)
-> `std.Err "couldn't spawn thread"
;;
-> `std.Ok (ret : tid)
}
-const initstack = {stk, fn, sz
- var tos, szp, fp, env, envsz
+const initstk = {stk, fn, sz
+ var len, tos, hdr, fp, env, envsz
+
+ len = tlslen()
+ tos = (stk : std.intptr) + (sz : std.intptr)
+ tos -= (sizeof(tlshdr) + ((len : std.intptr) * sizeof(void#)) + 0xf) & ~0xf
+ hdr = (tos : tlshdr#)
+ hdr.len = len
+ hdr.base = stk
+ hdr.stksz = sz
envsz = std.fnenvsz(fn)
- tos = (stk : std.intptr)
- tos -= sizeof(int64)
- szp = (tos : sys.size#)
- szp# = sz
tos -= (envsz : std.intptr)
env = tos
tos -= sizeof((->void))
fp = (tos : (->void)#)
fp# = std.fnbdup(fn, (env : byte#)[:envsz])
- -> (tos : byte#)
-}
-
-const getstk = {sz
- var p, m
-
- p = sys.mmap((0 : 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 : std.intptr)
- m += (sz : std.intptr)
- -> (m : byte#)
+ -> ((tos : byte#), hdr)
}
const startthread = {fn : (-> void)
diff --git a/lib/thread/spawn+openbsd.myr b/lib/thread/spawn+openbsd.myr
index 4526520..c63b51b 100644
--- a/lib/thread/spawn+openbsd.myr
+++ b/lib/thread/spawn+openbsd.myr
@@ -1,9 +1,11 @@
use std
use sys
-pkg thread =
- type tid = uint64
+use "common"
+use "tls"
+use "types"
+pkg thread =
const spawn : (fn : (-> void) -> std.result(tid, byte[:]))
pkglocal var exitstk : byte#
;;
@@ -18,6 +20,7 @@ const __init__ = {
time to swap to before we invalidate a stack.
*/
exitstk = getstk(16)
+ std.assert(exitstk != sys.Mapbad, "error: failed to mmap exitstk\n")
}
const spawn = {fn;
@@ -25,30 +28,17 @@ const spawn = {fn;
}
const spawnstk = {fn, sz
- var stk, szp, fp, tos, tfp, env, envsz
- var ret
+ var stk, tos, hdr, tfp, ret
stk = getstk(sz)
if stk == sys.Mapbad
-> `std.Err "couldn't get stack"
;;
- /* store size */
- tos = (stk : std.intptr)
- tos -= sizeof(int64)
- szp = (tos : sys.size#)
- szp# = Stacksz
-
- /* store func */
- envsz = std.fnenvsz(fn)
- tos -= (envsz : std.intptr)
- env = tos
- tos -= sizeof((->void))
- fp = (tos : (->void)#)
- fp# = std.fnbdup(fn, (env : byte#)[:envsz])
+ (tos, hdr) = initstk(stk, fn, sz)
tfp = [
- .tcb = (0 : void#),
- .tid = &ret,
+ .tcb = (hdr : void#),
+ .tid = (&hdr.tid : sys.pid#),
.stk = (tos : byte#),
]
ret = sys.__tfork_thread(&tfp,
@@ -56,22 +46,34 @@ const spawnstk = {fn, sz
(startthread : void#),
(0 : void#))
if ret < 0
+ sys.munmap(stk, sz)
-> `std.Err "couldn't spawn thread"
;;
-> `std.Ok (ret : tid)
}
-const getstk = {sz
- var p, m
+const initstk = {stk, fn, sz
+ var len, tos, hdr, fp, env, envsz
- p = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon | sys.Mstack, -1, 0)
- if p == sys.Mapbad
- -> p
- ;;
- /* stack starts at the top of memory and grows down. */
- m = (p : std.intptr)
- m += (sz : std.intptr)
- -> (m : byte#)
+ len = tlslen()
+ tos = (stk : std.intptr) + (sz : std.intptr)
+ tos -= (sizeof(tlshdr) + ((len : std.intptr) * sizeof(void#)) + 0xf) & ~0xf
+ hdr = (tos : tlshdr#)
+ hdr.len = len
+ hdr.base = stk
+ hdr.stksz = sz
+
+ envsz = std.fnenvsz(fn)
+ tos -= (envsz : std.intptr)
+ env = tos
+ tos -= sizeof((->void))
+ fp = (tos : (->void)#)
+ fp# = std.fnbdup(fn, (env : byte#)[:envsz])
+ -> ((tos : byte#), hdr)
+}
+
+const getstk = {sz
+ -> sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
}
const startthread = {fn : (-> void)
diff --git a/lib/thread/spawn+osx.myr b/lib/thread/spawn+osx.myr
index 417e64a..3e6ed16 100644
--- a/lib/thread/spawn+osx.myr
+++ b/lib/thread/spawn+osx.myr
@@ -1,9 +1,10 @@
use sys
use std
-pkg thread =
- type tid = uint64
+use "tls"
+use "types"
+pkg thread =
const spawn : (fn : (-> void) -> std.result(tid, byte[:]))
;;
@@ -34,34 +35,13 @@ const spawn = {fn
}
const spawnstk = {fn, sz
- var stk : byte#, tid, ret
- var szp, f, tos, env, envsz
+ var stk, tos, ret
- stk = getstk(sz)
+ stk = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
if stk == sys.Mapbad
-> `std.Err "couldn't get stack"
;;
- tid = -1
-
- /* find top of stack */
- tos = (stk : std.intptr) + (sz : std.intptr)
-
- /* store the stack size */
- tos -= sizeof(sys.size)
- sz -= sizeof(sys.size)
- szp = (tos : sys.size#)
- szp# = Stacksz
-
- /* store the function we call */
- envsz = std.fnenvsz(fn)
- tos -= (envsz : std.intptr)
- sz -= (envsz : sys.size)
- env = tos
- tos -= sizeof((->void))
- sz -= sizeof((->void))
- f = (tos : (->void)#)
- f# = std.fnbdup(fn, (env : byte#)[:envsz])
- var repr = (&fn : int64[2]#)#
+ tos = initstk(stk, fn, sz)
ret = sys.bsdthread_create( \
(tramp : void#), \ /* start */
@@ -70,21 +50,37 @@ const spawnstk = {fn, sz
(0 : void#), \ /* pthread struct */
0x01000000) /* flags (PTHREAD_START_CUSTOM): don't alloc stack in kernel */
- if ret == (-1 : void#)
+ if (ret : std.size) < 0
+ sys.munmap(stk, sz)
-> `std.Err "couldn't spawn thread"
;;
- -> `std.Ok (ret : tid)
+ -> `std.Ok (stk : tid)
}
-const getstk = {sz
- var p, m
+const initstk = {stk, fn, sz
+ var len, tos, hdr, fp, env, envsz
- p = sys.mmap((0 : byte#), sz, sys.Mprotrw, sys.Mpriv | sys.Manon, -1, 0)
- if p == sys.Mapbad
- -> p
- ;;
- m = (p : std.intptr)
- -> (m : byte#)
+ len = tlslen()
+ tos = (stk : std.intptr) + (sz : std.intptr)
+ tos -= (sizeof(tlshdr) + ((len : std.intptr) * sizeof(void#)) + 0xf) & ~0xf
+ hdr = (tos : tlshdr#)
+ hdr.tid = (stk : tid)
+ hdr.len = len
+ hdr.base = stk
+ hdr.stksz = sz
+
+ var fn1 = {
+ setgsbase(hdr)
+ fn()
+ }
+
+ envsz = std.fnenvsz(fn1)
+ tos -= (envsz : std.intptr)
+ env = tos
+ tos -= sizeof((->void))
+ fp = (tos : (->void)#)
+ fp# = std.fnbdup(fn1, (env : byte#)[:envsz])
+ -> (tos : byte#)
}
/*
diff --git a/lib/thread/start+osx-x64.s b/lib/thread/start+osx-x64.s
index bb497bb..081ed45 100644
--- a/lib/thread/start+osx-x64.s
+++ b/lib/thread/start+osx-x64.s
@@ -15,20 +15,13 @@ _thread$start:
/*
const thread.exit : (stacksz : std.size -> void)
-NOTE: must be called from the bottom of the stack, since
-we assume that %rbp is in the top 4k of the stack.
*/
.globl _thread$exit
_thread$exit:
- /* find top of stack */
- movq %rbp,%rdi /* addr */
- andq $~0xfff,%rdi /* align it */
- addq $0x1000,%rdi
-
/* munmap(base, size) */
movq $0x2000049,%rax /* munmap */
- movq -8(%rdi),%rsi /* size */
- subq %rsi,%rdi /* move to base ptr */
+ movq %gs:0x08,%rdi /* base */
+ movq %gs:0x10,%rsi /* stksz */
syscall
/* exit the thread */
diff --git a/lib/thread/test/die.myr b/lib/thread/test/die.myr
new file mode 100644
index 0000000..db0fb21
--- /dev/null
+++ b/lib/thread/test/die.myr
@@ -0,0 +1,8 @@
+use thread
+
+const main = {
+ var m = thread.mkmtx()
+ thread.mtxlock(&m)
+ thread.mtxunlock(&m)
+ thread.mtxunlock(&m)
+}
diff --git a/lib/thread/test/tls.myr b/lib/thread/test/tls.myr
new file mode 100644
index 0000000..fa11111
--- /dev/null
+++ b/lib/thread/test/tls.myr
@@ -0,0 +1,49 @@
+use std
+use sys
+use thread
+
+const Nelt = 100
+const Nthr = 100
+
+var elts : thread.tid[Nelt]
+var start
+var wg
+
+const setget = {
+ var tid = thread.tid()
+ var localelts : thread.tid[Nelt]
+ for var i = 0; i < Nelt; i++
+ localelts[i] = elts[i] + tid
+ ;;
+
+ var k = start
+ for var i = 0; i < Nelt; i++
+ thread.tlsset(k, &localelts[i])
+ k++
+ ;;
+ k = start
+ for var i = 0; i < Nelt; i++
+ std.assert(thread.tlsget(k)# == localelts[i], "tls is broken\n")
+ k++
+ ;;
+ thread.wgpost(&wg)
+}
+
+const main = {
+ for var i = 0; i < Nelt; i++
+ elts[i] = std.randnum()
+ ;;
+
+ start = thread.tlsalloc()
+ for var i = 1; i < Nelt; i++
+ var k : thread.tlskey(thread.tid#) = thread.tlsalloc()
+ ;;
+
+ wg = thread.mkwg(Nthr)
+ for var i = 1; i < 100; i++
+ thread.spawn(setget)
+ ;;
+ setget()
+
+ thread.wgwait(&wg)
+}
diff --git a/lib/thread/tls+fsbase.myr b/lib/thread/tls+fsbase.myr
new file mode 100644
index 0000000..0b59373
--- /dev/null
+++ b/lib/thread/tls+fsbase.myr
@@ -0,0 +1,59 @@
+use std
+
+use "common"
+use "fsbase"
+use "types"
+
+pkg thread =
+ generic tlsalloc : (-> tlskey(@a#))
+ generic tlsset : (k : tlskey(@a#), v : @a# -> void)
+ generic tlsget : (k : tlskey(@a#) -> @a#)
+ extern const tid : (-> tid)
+
+ pkglocal const tlsoob : (k : tlskey(void) -> void)
+ pkglocal extern const tlslen : (-> tlskey(void))
+;;
+
+const Staticcap = 8
+
+var _hdr
+var _cap = Staticcap
+
+generic tlsalloc = {
+ std.assert(tid() == 0, "error: tlsalloc must be called from main thread\n")
+ if _hdr == Zptr
+ /* `_hdr` is lazily initialized here since we can't set it in start.s */
+ _hdr = getfsbase()
+ ;;
+
+ if _hdr.len++ == _cap
+ std.assert(_cap < 0x8000_0000, "error: max tls slots exceeded\n")
+ var l = sizeof(tlshdr) + ((_cap : std.size) * sizeof(void#))
+ var h = std.bytealloc(sizeof(tlshdr) + ((_cap *= 2 : std.size) * sizeof(void#)))
+
+ std.memblit(h, (_hdr : byte#), l)
+ setfsbase((h : tlshdr#))
+ /* this is ugly... the initial tls region is statically allocated */
+ if _cap != Staticcap * 2
+ std.bytefree((_hdr : byte#), l)
+ ;;
+ _hdr = (h : tlshdr#)
+ ;;
+ -> (_hdr.len - 1 : tlskey(@a#))
+}
+
+generic tlsset = {k, v
+ _tlsset((k : tlskey(void)), (v : void#))
+}
+
+generic tlsget = {k
+ -> (_tlsget((k : tlskey(void))) : @a#)
+}
+
+const tlsoob = {k
+ std.fput(std.Err, "error: tlskey {} out of bounds {}\n", k, tlslen())
+ std.suicide()
+}
+
+extern const _tlsset : (k : tlskey(void), v : void# -> void)
+extern const _tlsget : (k : tlskey(void) -> void#)
diff --git a/lib/thread/tls+osx.myr b/lib/thread/tls+osx.myr
new file mode 100644
index 0000000..763f3f3
--- /dev/null
+++ b/lib/thread/tls+osx.myr
@@ -0,0 +1,70 @@
+use std
+
+use "common"
+use "types"
+
+pkg thread =
+ generic tlsalloc : (-> tlskey(@a#))
+ generic tlsset : (k : tlskey(@a#), v : @a# -> void)
+ generic tlsget : (k : tlskey(@a#) -> @a#)
+ extern const tid : (-> tid)
+
+ pkglocal const tlsoob : (k : tlskey(void) -> void)
+ pkglocal extern const tlslen : (-> tlskey(void))
+ pkglocal const setgsbase : (h : tlshdr# -> void)
+ pkglocal extern const getgsbase : (-> tlshdr#)
+;;
+
+const Staticcap = 8
+
+var _hdr
+var _cap = Staticcap
+
+generic tlsalloc = {
+ std.assert(tid() == 0, "error: tlsalloc must be called from main thread\n")
+ if _hdr == Zptr
+ /* `_hdr` is lazily initialized here since we can't set it in start.s */
+ _hdr = getgsbase()
+ ;;
+
+ if _hdr.len++ == _cap
+ std.assert(_cap < 0x8000_0000, "error: max tls slots exceeded\n")
+ var l = sizeof(tlshdr) + ((_cap : std.size) * sizeof(void#))
+ var h = std.bytealloc(sizeof(tlshdr) + ((_cap *= 2 : std.size) * sizeof(void#)))
+
+ std.memblit(h, (_hdr : byte#), l)
+ setgsbase((h : tlshdr#))
+ /* this is ugly... the initial tls region is statically allocated */
+ if _cap != Staticcap * 2
+ std.bytefree((_hdr : byte#), l)
+ ;;
+ _hdr = (h : tlshdr#)
+ ;;
+ -> (_hdr.len - 1 : tlskey(@a#))
+}
+
+generic tlsset = {k, v
+ _tlsset((k : tlskey(void)), (v : void#))
+}
+
+generic tlsget = {k
+ -> (_tlsget((k : tlskey(void))) : @a#)
+}
+
+const tlsoob = {k
+ std.fput(std.Err, "error: tlskey {} out of bounds {}\n", k, tlslen())
+ std.suicide()
+}
+
+const setgsbase = {h
+ match _setgsbase(h)
+ | 0xf: /* yes, this indicates success; no, it's not documented */
+ | err:
+ std.fput(std.Err, "error: setgsbase returned {}\n", err)
+ std.suicide()
+ ;;
+}
+
+extern const _tlsset : (k : tlskey(void), v : void# -> void)
+extern const _tlsget : (k : tlskey(void) -> void#)
+extern const _setgsbase : (h : tlshdr# -> int64)
diff --git a/lib/thread/tls-impl+fsbase-x64.s b/lib/thread/tls-impl+fsbase-x64.s
new file mode 100644
index 0000000..d34e421
--- /dev/null
+++ b/lib/thread/tls-impl+fsbase-x64.s
@@ -0,0 +1,48 @@
+.set tid, 0x00
+.set len, 0x04
+.set slots, 0x18
+
+/* const tid : (-> tid) */
+.globl thread$tid
+.globl _thread$tid
+thread$tid:
+_thread$tid:
+ movl %fs:tid, %eax
+ ret
+
+/* const _tlsset : (k : key, v : void# -> void) */
+.globl thread$_tlsset
+.globl _thread$_tlsset
+thread$_tlsset:
+_thread$_tlsset:
+ cmpl %fs:len, %edi
+ jnb oob
+
+ movslq %edi, %rdi
+ movq $slots, %r10
+ movq %rsi, %fs:(%r10, %rdi, 0x8)
+ ret
+
+/* const _tlsget : (k : key -> void#) */
+.globl thread$_tlsget
+.globl _thread$_tlsget
+thread$_tlsget:
+_thread$_tlsget:
+ cmpl %fs:len, %edi
+ jnb oob
+
+ movslq %edi, %rdi
+ movq $slots, %r10
+ movq %fs:(%r10, %rdi, 0x8), %rax
+ ret
+
+oob:
+ call thread$tlsoob
+
+/* const tlslen : (-> key) */
+.globl thread$tlslen
+.globl _thread$tlslen
+thread$tlslen:
+_thread$tlslen:
+ movl %fs:len, %eax
+ ret
diff --git a/lib/thread/tls-impl+osx-x64.s b/lib/thread/tls-impl+osx-x64.s
new file mode 100644
index 0000000..bbe7dcd
--- /dev/null
+++ b/lib/thread/tls-impl+osx-x64.s
@@ -0,0 +1,64 @@
+.set tid, 0x00
+.set len, 0x08
+.set self, 0x20
+.set slots, 0x28
+
+/* const tid : (-> tid) */
+.globl thread$tid
+.globl _thread$tid
+thread$tid:
+_thread$tid:
+ movq %gs:tid, %rax
+ ret
+
+/* const _tlsset : (k : key, v : void# -> void) */
+.globl thread$_tlsset
+.globl _thread$_tlsset
+thread$_tlsset:
+_thread$_tlsset:
+ cmpq %gs:len, %rdi
+ jnb oob
+
+ movq $slots, %r10
+ movq %rsi, %gs:(%r10, %rdi, 0x8)
+ ret
+
+/* const _tlsget : (k : key -> void#) */
+.globl thread$_tlsget
+.globl _thread$_tlsget
+thread$_tlsget:
+_thread$_tlsget:
+ cmpq %gs:len, %rdi
+ jnb oob
+
+ movq $slots, %r10
+ movq %gs:(%r10, %rdi, 0x8), %rax
+ ret
+
+oob:
+ call _thread$tlsoob
+
+/* const tlslen : (-> key) */
+.globl thread$tlslen
+.globl _thread$tlslen
+thread$tlslen:
+_thread$tlslen:
+ movq %gs:len, %rax
+ ret
+
+/* const _setgsbase : (h : tlshdr# -> int64) */
+.globl thread$_setgsbase
+.globl _thread$_setgsbase
+thread$_setgsbase:
+_thread$_setgsbase:
+ movq $0x3000003, %rax /* undocumented syscall; sets %gs to %rdi */
+ syscall
+ ret
+
+/* const getgsbase : (-> tlshdr#) */
+.globl thread$getgsbase
+.globl _thread$getgsbase
+thread$getgsbase:
+_thread$getgsbase:
+ movq %gs:self, %rax
+ ret
diff --git a/lib/thread/types+fsbase.myr b/lib/thread/types+fsbase.myr
new file mode 100644
index 0000000..1e3647c
--- /dev/null
+++ b/lib/thread/types+fsbase.myr
@@ -0,0 +1,19 @@
+use sys
+
+pkg thread =
+ type tid = sys.pid /* 32 bits on all of the fsbase platforms */
+ type tlskey(@a) = uint32
+
+ /*
+ XXX: Be sure to update tls-impl+fsbase.s and
+ rt/start-{freebsd,linux,netbsd,openbsd}.s if any changes are made to
+ the size of this struct and/or the offsets of any of its members.
+ */
+ pkglocal type tlshdr = struct
+ tid : tid
+ len : tlskey(void)
+ base : byte#
+ stksz : sys.size
+ slots : void#[...]
+ ;;
+;;
diff --git a/lib/thread/types+osx.myr b/lib/thread/types+osx.myr
new file mode 100644
index 0000000..bba067d
--- /dev/null
+++ b/lib/thread/types+osx.myr
@@ -0,0 +1,20 @@
+use sys
+
+pkg thread =
+ type tid = sys.pid /* 64 bits */
+ type tlskey(@a) = uint64
+
+ /*
+ XXX: Be sure to update tls-impl+osx.s and rt/start-osx.s if any changes
+ are made to the size of this struct and/or the offsets of any of its
+ members.
+ */
+ pkglocal type tlshdr = struct
+ tid : tid
+ len : tlskey(void)
+ base : byte#
+ stksz : sys.size
+ self : tlshdr#
+ slots : void#[...]
+ ;;
+;;