diff options
Diffstat (limited to 'lib')
32 files changed, 714 insertions, 192 deletions
diff --git a/lib/sys/sys+freebsd-x64.myr b/lib/sys/sys+freebsd-x64.myr index b89e99b..73821b4 100644 --- a/lib/sys/sys+freebsd-x64.myr +++ b/lib/sys/sys+freebsd-x64.myr @@ -38,6 +38,7 @@ pkg sys = type cpulevel = int type cpusetid = int type idtype = int + type sysarchop = int64 type acltype = int type acltag = uint32 @@ -802,6 +803,13 @@ pkg sys = const Siglwp : signo = Sigthr const Siglibrt : signo = 33 /* reserved by real-time library. */ + /* sysarch ops */ + const Archamd64getfs : sysarchop = 128 + const Archamd64setfs : sysarchop = 129 + const Archamd64getgs : sysarchop = 130 + const Archamd64setgs : sysarchop = 131 + const Archamd64getxfpu : sysarchop = 132 + extern const syscall : (sc:scno, args:... -> int64) extern var __cenvp : byte## @@ -1285,7 +1293,7 @@ pkg sys = const quotactl : (path : byte#, cmd : int, uid : int, arg : void# -> int) const lgetfh : (fname : byte#, fhp : fhandle# -> int) const getfh : (fname : byte#, fhp : fhandle# -> int) - const sysarch : (op : int, parms : byte# -> int) + const sysarch : (op : sysarchop, parms : void## -> int) const rtprio : (function : int, pid : pid, rtp : rtprio# -> int) const setfib : (fibnum : int -> int) const ntp_adjtime : (tp : timex# -> int) @@ -1969,7 +1977,7 @@ const getfh = {fname, fhp -> (syscall(Sysgetfh, a(fname), a(fhp)) : int) } const sysarch = {op, parms - -> (syscall(Syssysarch, a(op), a(parms)) : int) + -> (syscall(Syssysarch, op, a(parms)) : int) } const rtprio = {function, pid, rtp -> (syscall(Sysrtprio, a(function), a(pid), a(rtp)) : int) diff --git a/lib/sys/sys+linux-x64.myr b/lib/sys/sys+linux-x64.myr index 61bb8b2..bbfb689 100644 --- a/lib/sys/sys+linux-x64.myr +++ b/lib/sys/sys+linux-x64.myr @@ -45,6 +45,7 @@ pkg sys = type mfdflags = uint32 type aiocontext = uint64 type msg = void# + type arch_prctlop = uint64 type clock = union @@ -590,6 +591,12 @@ pkg sys = /* return value for a failed mapping */ const Mapbad : byte# = (-1 : byte#) + + /* arch_prctl ops */ + const Archsetgs : arch_prctlop = 0x1001 + const Archsetfs : arch_prctlop = 0x1002 + const Archgetfs : arch_prctlop = 0x1003 + const Archgetgs : arch_prctlop = 0x1004 /* signal flags */ const Sanocldstop : sigflags = 0x00000001 @@ -1097,6 +1104,7 @@ pkg sys = const Sysmq_notify : scno = 244 const Sysmq_getsetattr : scno = 245 const Sysprctl : scno = 157 + const Sysarch_prctl : scno = 158 const Sysswapon : scno = 167 const Sysswapoff : scno = 168 const Sys_sysctl : scno = 156 @@ -1308,7 +1316,7 @@ pkg sys = const settimeofday : (tv : timeval#, tz : timezone# -> int64) const adjtimex : (txc_p : timex# -> int64) const times : (tbuf : tms# -> int64) - const gettid : ( -> int64) + const gettid : ( -> pid) const alarm : (seconds : uint -> int64) const getppid : ( -> int64) const geteuid : ( -> int64) @@ -1484,6 +1492,7 @@ pkg sys = const mq_notify : (mqdes : int, notification : sigevent# -> int64) const mq_getsetattr : (mqdes : int, mqstat : mq_attr#, omqstat : mq_attr# -> int64) const prctl : (option : int, arg2 : uint64, arg3 : uint64, arg4 : uint64, arg5 : uint64 -> int64) + const arch_prctl : (op : arch_prctlop, addr : void# -> int64) const swapon : (specialfile : byte#, swap_flags : int -> int64) const swapoff : (specialfile : byte# -> int64) const _sysctl : (args : sysctl_args# -> int64) @@ -1782,7 +1791,7 @@ const times = {tbuf -> (syscall(Systimes, a(tbuf)) : int64) } const gettid = { - -> (syscall(Sysgettid) : int64) + -> (syscall(Sysgettid) : pid) } const alarm = {seconds -> (syscall(Sysalarm, a(seconds)) : int64) @@ -2309,6 +2318,9 @@ const mq_getsetattr = {mqdes, mqstat, omqstat const prctl = {option, arg2, arg3, arg4, arg5 -> (syscall(Sysprctl, a(option), a(arg2), a(arg3), a(arg4), a(arg5)) : int64) } +const arch_prctl = {op, addr + -> syscall(Sysarch_prctl, op, addr) +} const swapon = {specialfile, swap_flags -> (syscall(Sysswapon, a(specialfile), a(swap_flags)) : int64) } diff --git a/lib/sys/sys+netbsd-x64.myr b/lib/sys/sys+netbsd-x64.myr index 8e22c0b..5fd3bb0 100644 --- a/lib/sys/sys+netbsd-x64.myr +++ b/lib/sys/sys+netbsd-x64.myr @@ -18,6 +18,7 @@ pkg sys = type umtxop = int32 type signo = int32 type sigflags = int32 + type sysarchop = int64 type clock = union `Clockrealtime @@ -344,6 +345,21 @@ pkg sys = const Umtxmtxwake2 : umtxop = 22 const Umtxmax : umtxop = 23 + /* sysarch ops */ + const X8664getldt : sysarchop = 0 + const X8664setldt : sysarchop = 1 + const X8664iopl : sysarchop = 2 + const X8664getioperm : sysarchop = 3 + const X8664setioperm : sysarchop = 4 + const X8664oldvm86 : sysarchop = 5 + const X8664getmtrr : sysarchop = 11 + const X8664setmtrr : sysarchop = 12 + const X8664vm86 : sysarchop = 13 + const X8664getgsbase : sysarchop = 14 + const X8664getfsbase : sysarchop = 15 + const X8664setgsbase : sysarchop = 16 + const X8664setfsbase : sysarchop = 17 + /* signal actions */ const Saonstack : sigflags = 0x0001 /* take signal on signal stack */ const Sarestart : sigflags = 0x0002 /* restart system call on signal return */ @@ -908,6 +924,9 @@ pkg sys = new : void#, newsz : size# \ -> int) + /* misc */ + const sysarch : (op : sysarchop, args : void## -> int) + extern const cstring : (str : byte[:] -> byte#) /* filled by start code */ extern var __cenvp : byte## @@ -1102,6 +1121,10 @@ const sysctl = {mib, old, oldsz, new, newsz (mib : int#), a(mib.len), old, oldsz, new, newsz) : int) } +const sysarch = {op, args + -> (syscall(Syssysarch, op, args) : int) +} + const clockid = {clk match clk | `Clockrealtime: -> 0 diff --git a/lib/sys/sys+openbsd-x64.myr b/lib/sys/sys+openbsd-x64.myr index a260134..d840608 100644 --- a/lib/sys/sys+openbsd-x64.myr +++ b/lib/sys/sys+openbsd-x64.myr @@ -215,7 +215,7 @@ pkg sys = const Mfixed : mopt = 0x10 const Mfile : mopt = 0x0 const Manon : mopt = 0x1000 - const Mstack : mopt = 0x4000 + const Mstack : mopt = 0x0 const Mnoreplace : mopt = 0x0800 /* file types */ diff --git a/lib/sys/sys+openbsd:6.1-x64.myr b/lib/sys/sys+openbsd:6.1-x64.myr index 9314394..9ac648a 100644 --- a/lib/sys/sys+openbsd:6.1-x64.myr +++ b/lib/sys/sys+openbsd:6.1-x64.myr @@ -1031,7 +1031,7 @@ pkg sys = const symlinkat : (path : byte#, fd : int, link : byte# -> int) const unlinkat : (fd : int, path : byte#, flag : int -> int) const __set_tcb : (tcb : void# -> void) - const __get_tcb : ( -> void) + const __get_tcb : ( -> void#) ;; /* start manual overrides { */ @@ -1750,5 +1750,5 @@ const __set_tcb = {tcb -> (syscall(Sys__set_tcb, a(tcb)) : void) } const __get_tcb = { - -> (syscall(Sys__get_tcb) : void) + -> (syscall(Sys__get_tcb) : void#) } diff --git a/lib/sys/sys+openbsd:6.2-x64.myr b/lib/sys/sys+openbsd:6.2-x64.myr index 598d381..d89b702 100644 --- a/lib/sys/sys+openbsd:6.2-x64.myr +++ b/lib/sys/sys+openbsd:6.2-x64.myr @@ -348,7 +348,7 @@ pkg sys = const Mfixed : mopt = 0x10 const Mfile : mopt = 0x0 const Manon : mopt = 0x1000 - const Mstack : mopt = 0x4000 + const Mstack : mopt = 0x0 const Mnoreplace : mopt = 0x0800 /* file types */ @@ -1037,7 +1037,7 @@ pkg sys = const symlinkat : (path : byte#, fd : int, link : byte# -> int) const unlinkat : (fd : int, path : byte#, flag : int -> int) const __set_tcb : (tcb : void# -> void) - const __get_tcb : ( -> void) + const __get_tcb : ( -> void#) ;; /* start manual overrides { */ @@ -1759,5 +1759,5 @@ const __set_tcb = {tcb -> (syscall(Sys__set_tcb, a(tcb)) : void) } const __get_tcb = { - -> (syscall(Sys__get_tcb) : void) + -> (syscall(Sys__get_tcb) : void#) } diff --git a/lib/sys/sys+openbsd:6.3-x64.myr b/lib/sys/sys+openbsd:6.3-x64.myr index d013643..ac7170b 100644 --- a/lib/sys/sys+openbsd:6.3-x64.myr +++ b/lib/sys/sys+openbsd:6.3-x64.myr @@ -1036,7 +1036,7 @@ pkg sys = const symlinkat : (path : byte#, fd : int, link : byte# -> int) const unlinkat : (fd : int, path : byte#, flag : int -> int) const __set_tcb : (tcb : void# -> void) - const __get_tcb : ( -> void) + const __get_tcb : ( -> void#) ;; /* start manual overrides { */ @@ -1755,5 +1755,5 @@ const __set_tcb = {tcb -> (syscall(Sys__set_tcb, a(tcb)) : void) } const __get_tcb = { - -> (syscall(Sys__get_tcb) : void) + -> (syscall(Sys__get_tcb) : void#) } 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#[...] + ;; +;; |