89 lines
1.9 KiB
Go
89 lines
1.9 KiB
Go
//go:build llgo
|
|
|
|
package tls
|
|
|
|
import (
|
|
"unsafe"
|
|
|
|
c "github.com/goplus/llgo/runtime/internal/clite"
|
|
"github.com/goplus/llgo/runtime/internal/clite/pthread"
|
|
)
|
|
|
|
type Handle[T any] struct {
|
|
key pthread.Key
|
|
destructor func(*T)
|
|
}
|
|
|
|
// Alloc creates a TLS handle backed by pthread TLS.
|
|
func Alloc[T any](destructor func(*T)) Handle[T] {
|
|
var key pthread.Key
|
|
if ret := key.Create(slotDestructor[T]); ret != 0 {
|
|
c.Fprintf(c.Stderr, c.Str("tls: pthread_key_create failed (errno=%d)\n"), ret)
|
|
panic("tls: failed to create thread local storage key")
|
|
}
|
|
return Handle[T]{key: key, destructor: destructor}
|
|
}
|
|
|
|
// Get returns the value stored in the current thread's slot.
|
|
func (h Handle[T]) Get() T {
|
|
if ptr := h.key.Get(); ptr != nil {
|
|
return (*slot[T])(ptr).value
|
|
}
|
|
var zero T
|
|
return zero
|
|
}
|
|
|
|
// Set stores v in the current thread's slot, creating it if necessary.
|
|
func (h Handle[T]) Set(v T) {
|
|
s := h.ensureSlot()
|
|
s.value = v
|
|
}
|
|
|
|
// Clear zeroes the current thread's slot value without freeing the slot.
|
|
func (h Handle[T]) Clear() {
|
|
if ptr := h.key.Get(); ptr != nil {
|
|
s := (*slot[T])(ptr)
|
|
var zero T
|
|
s.value = zero
|
|
}
|
|
}
|
|
|
|
func (h Handle[T]) ensureSlot() *slot[T] {
|
|
if ptr := h.key.Get(); ptr != nil {
|
|
return (*slot[T])(ptr)
|
|
}
|
|
size := unsafe.Sizeof(slot[T]{})
|
|
mem := c.Calloc(1, size)
|
|
if mem == nil {
|
|
panic("tls: failed to allocate thread slot")
|
|
}
|
|
s := (*slot[T])(mem)
|
|
s.destructor = h.destructor
|
|
if existing := h.key.Get(); existing != nil {
|
|
c.Free(mem)
|
|
return (*slot[T])(existing)
|
|
}
|
|
if ret := h.key.Set(mem); ret != 0 {
|
|
c.Free(mem)
|
|
c.Fprintf(c.Stderr, c.Str("tls: pthread_setspecific failed (errno=%d)\n"), ret)
|
|
panic("tls: failed to set thread local storage value")
|
|
}
|
|
registerSlot(s)
|
|
return s
|
|
}
|
|
|
|
func slotDestructor[T any](ptr c.Pointer) {
|
|
s := (*slot[T])(ptr)
|
|
if s == nil {
|
|
return
|
|
}
|
|
if s.destructor != nil {
|
|
s.destructor(&s.value)
|
|
}
|
|
deregisterSlot(s)
|
|
var zero T
|
|
s.value = zero
|
|
s.destructor = nil
|
|
c.Free(ptr)
|
|
}
|