shithub: mc

ref: 330b2e6a59402d3a290629f409d77c2692973f3c
dir: /lib/thread/tls+osx.myr/

View raw version
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)