purego: restore sync.Pool (#457)
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
- commit
- d339eb0fd4d3c57f9b641f4781615650183c7043
- author
- Alexander Yastrebov · · 2026-06-04 01:59 UTC
- committer
- GitHub · · 2026-06-04 01:59 UTC
- parent
- 1512f32
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: v0.11.0-alpha.4
4 files · +20 -5
M func.go +11 -2
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | "math" | |
| 11 | 11 | "reflect" | |
| 12 | 12 | "runtime" | |
| 13 | + | "sync" | |
| 13 | 14 | "unsafe" | |
| 14 | 15 | ||
| 15 | 16 | "github.com/ebitengine/purego/internal/strings" | |
| @@ -21,6 +22,10 @@ const ( | |||
| 21 | 22 | align8ByteSize = 8 // 8-byte alignment boundary | |
| 22 | 23 | ) | |
| 23 | 24 | ||
| 25 | + | var thePool = sync.Pool{New: func() any { | |
| 26 | + | return new(syscallArgs) | |
| 27 | + | }} | |
| 28 | + | ||
| 24 | 29 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | |
| 25 | 30 | // It panics if it can't find the name symbol. | |
| 26 | 31 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | |
| @@ -318,12 +323,13 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 318 | 323 | var syscall *syscallArgs | |
| 319 | 324 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | |
| 320 | 325 | // Windows amd64, 386, and arm use syscall.SyscallN. | |
| 321 | - | syscall = &syscallArgs{} | |
| 326 | + | syscall = thePool.Get().(*syscallArgs) | |
| 322 | 327 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | |
| 323 | 328 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | |
| 324 | 329 | } else { | |
| 325 | 330 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | |
| 326 | 331 | } | |
| 332 | + | defer thePool.Put(syscall) | |
| 327 | 333 | if ty.NumOut() == 0 { | |
| 328 | 334 | return nil | |
| 329 | 335 | } | |
| @@ -340,7 +346,10 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 340 | 346 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |
| 341 | 347 | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) | |
| 342 | 348 | case reflect.Pointer: | |
| 343 | - | v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem() | |
| 349 | + | // Copy syscall.a1 into a local variable to prevent v | |
| 350 | + | // from holding a pointer to the pooled syscallArgs field. | |
| 351 | + | a1 := syscall.a1 | |
| 352 | + | v = reflect.NewAt(outType, unsafe.Pointer(&a1)).Elem() | |
| 344 | 353 | case reflect.Func: | |
| 345 | 354 | // wrap this C function in a nicely typed Go function | |
| 346 | 355 | v = reflect.New(outType) | |
| @@ -10,6 +10,7 @@ import ( | |||||
| 10 | "math" | 10 | "math" | ||
| 11 | "reflect" | 11 | "reflect" | ||
| 12 | "runtime" | 12 | "runtime" | ||
| 13 | + | "sync" | |||
| 13 | "unsafe" | 14 | "unsafe" | ||
| 14 | 15 | ||||
| 15 | "github.com/ebitengine/purego/internal/strings" | 16 | "github.com/ebitengine/purego/internal/strings" | ||
| @@ -21,6 +22,10 @@ const ( | |||||
| 21 | align8ByteSize = 8 // 8-byte alignment boundary | 22 | align8ByteSize = 8 // 8-byte alignment boundary | ||
| 22 | ) | 23 | ) | ||
| 23 | 24 | ||||
| 25 | + | var thePool = sync.Pool{New: func() any { | |||
| 26 | + | return new(syscallArgs) | |||
| 27 | + | }} | |||
| 28 | + | ||||
| 24 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | 29 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | ||
| 25 | // It panics if it can't find the name symbol. | 30 | // It panics if it can't find the name symbol. | ||
| 26 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | 31 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | ||
| @@ -318,12 +323,13 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 318 | var syscall *syscallArgs | 323 | var syscall *syscallArgs | ||
| 319 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | 324 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | ||
| 320 | // Windows amd64, 386, and arm use syscall.SyscallN. | 325 | // Windows amd64, 386, and arm use syscall.SyscallN. | ||
| 321 | - | syscall = &syscallArgs{} | 326 | + | syscall = thePool.Get().(*syscallArgs) |
| 322 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | 327 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | ||
| 323 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | 328 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | ||
| 324 | } else { | 329 | } else { | ||
| 325 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | 330 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | ||
| 326 | } | 331 | } | ||
| 332 | + | defer thePool.Put(syscall) | |||
| 327 | if ty.NumOut() == 0 { | 333 | if ty.NumOut() == 0 { | ||
| 328 | return nil | 334 | return nil | ||
| 329 | } | 335 | } | ||
| @@ -340,7 +346,10 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 340 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | 346 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | ||
| 341 | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) | 347 | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) | ||
| 342 | case reflect.Pointer: | 348 | case reflect.Pointer: | ||
| 343 | - | v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem() | 349 | + | // Copy syscall.a1 into a local variable to prevent v |
| 350 | + | // from holding a pointer to the pooled syscallArgs field. | |||
| 351 | + | a1 := syscall.a1 | |||
| 352 | + | v = reflect.NewAt(outType, unsafe.Pointer(&a1)).Elem() | |||
| 344 | case reflect.Func: | 353 | case reflect.Func: | ||
| 345 | // wrap this C function in a nicely typed Go function | 354 | // wrap this C function in a nicely typed Go function | ||
| 346 | v = reflect.New(outType) | 355 | v = reflect.New(outType) | ||
M syscall.go +3 -1
| @@ -22,7 +22,8 @@ type syscallArgs struct { | |||
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 25 | - | s := &syscallArgs{ | |
| 25 | + | s := thePool.Get().(*syscallArgs) | |
| 26 | + | *s = syscallArgs{ | |
| 26 | 27 | fn: fn, | |
| 27 | 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 28 | 29 | 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) { | |||
| 78 | 79 | var floats [maxArgs]uintptr | |
| 79 | 80 | copy(floats[:], tmp[:]) | |
| 80 | 81 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 82 | + | defer thePool.Put(s) | |
| 81 | 83 | return s.a1, s.a2, s.a3 | |
| 82 | 84 | } | |
| @@ -22,7 +22,8 @@ type syscallArgs struct { | |||||
| 22 | } | 22 | } | ||
| 23 | 23 | ||||
| 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | ||
| 25 | - | s := &syscallArgs{ | 25 | + | s := thePool.Get().(*syscallArgs) |
| 26 | + | *s = syscallArgs{ | |||
| 26 | fn: fn, | 27 | fn: fn, | ||
| 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 29 | 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) { | |||||
| 78 | var floats [maxArgs]uintptr | 79 | var floats [maxArgs]uintptr | ||
| 79 | copy(floats[:], tmp[:]) | 80 | copy(floats[:], tmp[:]) | ||
| 80 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 81 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 82 | + | defer thePool.Put(s) | |||
| 81 | return s.a1, s.a2, s.a3 | 83 | return s.a1, s.a2, s.a3 | ||
| 82 | } | 84 | } | ||
M syscall_32bit.go +3 -1
| @@ -22,7 +22,8 @@ type syscallArgs struct { | |||
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 25 | - | s := &syscallArgs{ | |
| 25 | + | s := thePool.Get().(*syscallArgs) | |
| 26 | + | *s = syscallArgs{ | |
| 26 | 27 | fn: fn, | |
| 27 | 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 28 | 29 | 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) { | |||
| 80 | 81 | var floats [16]uintptr | |
| 81 | 82 | copy(floats[:], tmp[:16]) | |
| 82 | 83 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 84 | + | defer thePool.Put(s) | |
| 83 | 85 | return s.a1, s.a2, s.a3 | |
| 84 | 86 | } | |
| @@ -22,7 +22,8 @@ type syscallArgs struct { | |||||
| 22 | } | 22 | } | ||
| 23 | 23 | ||||
| 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | ||
| 25 | - | s := &syscallArgs{ | 25 | + | s := thePool.Get().(*syscallArgs) |
| 26 | + | *s = syscallArgs{ | |||
| 26 | fn: fn, | 27 | fn: fn, | ||
| 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 29 | 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) { | |||||
| 80 | var floats [16]uintptr | 81 | var floats [16]uintptr | ||
| 81 | copy(floats[:], tmp[:16]) | 82 | copy(floats[:], tmp[:16]) | ||
| 82 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 83 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 84 | + | defer thePool.Put(s) | |||
| 83 | return s.a1, s.a2, s.a3 | 85 | return s.a1, s.a2, s.a3 | ||
| 84 | } | 86 | } | ||
M syscall_ppc64le.go +3 -1
| @@ -20,7 +20,8 @@ type syscallArgs struct { | |||
| 20 | 20 | } | |
| 21 | 21 | ||
| 22 | 22 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 23 | - | s := &syscallArgs{ | |
| 23 | + | s := thePool.Get().(*syscallArgs) | |
| 24 | + | *s = syscallArgs{ | |
| 24 | 25 | fn: fn, | |
| 25 | 26 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 26 | 27 | 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) { | |||
| 66 | 67 | var floats [maxArgs]uintptr | |
| 67 | 68 | copy(floats[:], tmp[:]) | |
| 68 | 69 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 70 | + | defer thePool.Put(s) | |
| 69 | 71 | return s.a1, s.a2, s.a3 | |
| 70 | 72 | } | |
| @@ -20,7 +20,8 @@ type syscallArgs struct { | |||||
| 20 | } | 20 | } | ||
| 21 | 21 | ||||
| 22 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | 22 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | ||
| 23 | - | s := &syscallArgs{ | 23 | + | s := thePool.Get().(*syscallArgs) |
| 24 | + | *s = syscallArgs{ | |||
| 24 | fn: fn, | 25 | fn: fn, | ||
| 25 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 26 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 26 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 27 | 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) { | |||||
| 66 | var floats [maxArgs]uintptr | 67 | var floats [maxArgs]uintptr | ||
| 67 | copy(floats[:], tmp[:]) | 68 | copy(floats[:], tmp[:]) | ||
| 68 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 69 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 70 | + | defer thePool.Put(s) | |||
| 69 | return s.a1, s.a2, s.a3 | 71 | return s.a1, s.a2, s.a3 | ||
| 70 | } | 72 | } | ||