Files
zzet--gortex/internal/thirdparty/go-pointer/pointer.go
T
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

108 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package pointer is a drop-in, sharded replacement for
// github.com/mattn/go-pointer. The upstream implementation guards a
// single map[unsafe.Pointer]any with one global RWMutex; every CGo
// payload-passing call site (notably tree-sitter's
// Parser.ParseWithOptions) takes that lock. Under parallel parsing
// load that single mutex becomes the throughput ceiling — the
// daemon's cold-start profile showed 71 of ~100 goroutines blocked
// on it at any moment.
//
// This implementation keeps the upstream API verbatim (Save / Restore
// / Unref with the same signatures) so we can swap it in via a
// go.mod `replace` directive without touching go-tree-sitter or any
// other dependency. The store is split into numShards independent
// (mutex, map) pairs; the shard for a given pointer is chosen from
// the pointer's address bits, so concurrent Save calls naturally
// distribute across shards because C.malloc returns distinct
// addresses for distinct allocations.
//
// numShards is intentionally over-provisioned (64) — the cost per
// shard is the unused empty map header plus a zero-value Mutex, and
// the parser worker pool is sized to NumCPU which on modern boxes
// can sit at 1216; having room beyond that means even a future
// expansion of the worker pool finds free shards.
package pointer
// #include <stdlib.h>
import "C"
import (
"sync"
"unsafe"
)
const numShards = 64
type shard struct {
mu sync.Mutex
store map[unsafe.Pointer]any
}
var shards [numShards]shard
func init() {
for i := range shards {
shards[i].store = make(map[unsafe.Pointer]any)
}
}
// shardFor picks a shard from the pointer's address. C.malloc results
// are at least 8-byte aligned on every platform Go supports, so
// shifting off the low 3 bits removes the alignment artefact and the
// modulus then spreads addresses across all 64 shards.
func shardFor(ptr unsafe.Pointer) *shard {
return &shards[(uintptr(ptr)>>3)%numShards]
}
// Save stores v and returns a unique C pointer that can be passed
// across CGo and later resolved back to v via Restore. Returns nil
// when v is nil to match the upstream contract used by callers that
// pass optional payloads through.
func Save(v any) unsafe.Pointer {
if v == nil {
return nil
}
// One-byte malloc gives us a unique, valid C pointer to use as the
// store key. The C side never dereferences the byte — it only
// passes the pointer back to our Restore callback. The body of
// the value v lives in Go memory; this pointer is just the index.
ptr := unsafe.Pointer(C.malloc(C.size_t(1)))
if ptr == nil {
panic("go-pointer: malloc returned nil")
}
s := shardFor(ptr)
s.mu.Lock()
s.store[ptr] = v
s.mu.Unlock()
return ptr
}
// Restore returns the value previously stored under ptr by Save, or
// nil when ptr is nil or has been Unref'd.
func Restore(ptr unsafe.Pointer) any {
if ptr == nil {
return nil
}
s := shardFor(ptr)
s.mu.Lock()
v := s.store[ptr]
s.mu.Unlock()
return v
}
// Unref drops the value stored under ptr and frees the underlying C
// allocation. Safe to call with nil. Pairs 1:1 with Save — callers
// typically defer Unref(ptr) right after Save.
func Unref(ptr unsafe.Pointer) {
if ptr == nil {
return
}
s := shardFor(ptr)
s.mu.Lock()
delete(s.store, ptr)
s.mu.Unlock()
C.free(ptr)
}