From 4e54827b0369d41fe995764bb9d2f8695fe9faed Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 28 May 2026 01:09:43 +0200 Subject: [PATCH] func.go: restore sync.Pool Revert the sync.Pool removal #452 and copy syscall.a1 into a local variable before taking its address, so the reflect.Value points to an independent location that is not affected by pool recycling. Updates #451 --- func.go | 13 +++++++++++-- syscall.go | 4 +++- syscall_32bit.go | 4 +++- syscall_ppc64le.go | 4 +++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/func.go b/func.go index 5ce5103a..11df76dd 100644 --- a/func.go +++ b/func.go @@ -10,6 +10,7 @@ import ( "math" "reflect" "runtime" + "sync" "unsafe" "github.com/ebitengine/purego/internal/strings" @@ -21,6 +22,10 @@ const ( align8ByteSize = 8 // 8-byte alignment boundary ) +var thePool = sync.Pool{New: func() any { + return new(syscallArgs) +}} + // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). // It panics if it can't find the name symbol. func RegisterLibFunc(fptr any, handle uintptr, name string) { @@ -318,12 +323,13 @@ func RegisterFunc(fptr any, cfn uintptr) { var syscall *syscallArgs if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { // Windows amd64, 386, and arm use syscall.SyscallN. - syscall = &syscallArgs{} + syscall = thePool.Get().(*syscallArgs) syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support } else { syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) } + defer thePool.Put(syscall) if ty.NumOut() == 0 { return nil } @@ -340,7 +346,10 @@ func RegisterFunc(fptr any, cfn uintptr) { // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) case reflect.Pointer: - v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem() + // Copy syscall.a1 into a local variable to prevent v + // from holding a pointer to the pooled syscallArgs field. + a1 := syscall.a1 + v = reflect.NewAt(outType, unsafe.Pointer(&a1)).Elem() case reflect.Func: // wrap this C function in a nicely typed Go function v = reflect.New(outType) diff --git a/syscall.go b/syscall.go index 06068231..32f4a3d9 100644 --- a/syscall.go +++ b/syscall.go @@ -22,7 +22,8 @@ type syscallArgs struct { } func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { - s := &syscallArgs{ + s := thePool.Get().(*syscallArgs) + *s = syscallArgs{ fn: fn, a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], @@ -78,5 +79,6 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { var floats [maxArgs]uintptr copy(floats[:], tmp[:]) s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) return s.a1, s.a2, s.a3 } diff --git a/syscall_32bit.go b/syscall_32bit.go index d6667da5..c0641110 100644 --- a/syscall_32bit.go +++ b/syscall_32bit.go @@ -22,7 +22,8 @@ type syscallArgs struct { } func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { - s := &syscallArgs{ + s := thePool.Get().(*syscallArgs) + *s = syscallArgs{ fn: fn, a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], @@ -80,5 +81,6 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { var floats [16]uintptr copy(floats[:], tmp[:16]) s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) return s.a1, s.a2, s.a3 } diff --git a/syscall_ppc64le.go b/syscall_ppc64le.go index a8e94a2c..a133be3a 100644 --- a/syscall_ppc64le.go +++ b/syscall_ppc64le.go @@ -20,7 +20,8 @@ type syscallArgs struct { } func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { - s := &syscallArgs{ + s := thePool.Get().(*syscallArgs) + *s = syscallArgs{ fn: fn, a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], @@ -66,5 +67,6 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { var floats [maxArgs]uintptr copy(floats[:], tmp[:]) s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) return s.a1, s.a2, s.a3 }