Files
llgo/runtime/internal/clite/tls/tls_common.go
2025-10-15 12:21:51 +08:00

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)
}