tls: add gc-aware pthread slots
This commit is contained in:
88
runtime/internal/clite/tls/tls_common.go
Normal file
88
runtime/internal/clite/tls/tls_common.go
Normal file
@@ -0,0 +1,88 @@
|
||||
//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)
|
||||
}
|
||||
Reference in New Issue
Block a user