purego: remove pooling of syscall (#452) (#453)
(cherry picked from commit 11272d2804cdba6e4e4e6635e80f035a14f9ccbe) Co-authored-by: TotallyGamerJet <TotallyGamerJet@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- commit
- eaff005fcda6dae0d855da9c6ee3efb7e775ddad
- author
- Hajime Hoshi · · 2026-05-24 13:19 UTC
- committer
- GitHub · · 2026-05-24 13:19 UTC
- parent
- bb9b1f4
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: upstream-v0.10.1tag: v0.10.1
3 files · +37 -12
M func.go +1 -8
| @@ -10,7 +10,6 @@ import ( | |||
| 10 | 10 | "math" | |
| 11 | 11 | "reflect" | |
| 12 | 12 | "runtime" | |
| 13 | - | "sync" | |
| 14 | 13 | "unsafe" | |
| 15 | 14 | ||
| 16 | 15 | "github.com/ebitengine/purego/internal/strings" | |
| @@ -22,10 +21,6 @@ const ( | |||
| 22 | 21 | align8ByteSize = 8 // 8-byte alignment boundary | |
| 23 | 22 | ) | |
| 24 | 23 | ||
| 25 | - | var thePool = sync.Pool{New: func() any { | |
| 26 | - | return new(syscall15Args) | |
| 27 | - | }} | |
| 28 | - | ||
| 29 | 24 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | |
| 30 | 25 | // It panics if it can't find the name symbol. | |
| 31 | 26 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | |
| @@ -310,8 +305,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 310 | 305 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | |
| 311 | 306 | } | |
| 312 | 307 | ||
| 313 | - | syscall := thePool.Get().(*syscall15Args) | |
| 314 | - | defer thePool.Put(syscall) | |
| 308 | + | syscall := &syscall15Args{} | |
| 315 | 309 | ||
| 316 | 310 | if runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" { | |
| 317 | 311 | syscall.Set(cfn, sysargs[:], floats[:], 0) | |
| @@ -321,7 +315,6 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 321 | 315 | syscall.Set(cfn, sysargs[:], floats[:], arm64_r8) | |
| 322 | 316 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall)) | |
| 323 | 317 | } else { | |
| 324 | - | *syscall = syscall15Args{} | |
| 325 | 318 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | |
| 326 | 319 | syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | |
| 327 | 320 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | |
| @@ -10,7 +10,6 @@ import ( | |||||
| 10 | "math" | 10 | "math" | ||
| 11 | "reflect" | 11 | "reflect" | ||
| 12 | "runtime" | 12 | "runtime" | ||
| 13 | - | "sync" | |||
| 14 | "unsafe" | 13 | "unsafe" | ||
| 15 | 14 | ||||
| 16 | "github.com/ebitengine/purego/internal/strings" | 15 | "github.com/ebitengine/purego/internal/strings" | ||
| @@ -22,10 +21,6 @@ const ( | |||||
| 22 | align8ByteSize = 8 // 8-byte alignment boundary | 21 | align8ByteSize = 8 // 8-byte alignment boundary | ||
| 23 | ) | 22 | ) | ||
| 24 | 23 | ||||
| 25 | - | var thePool = sync.Pool{New: func() any { | |||
| 26 | - | return new(syscall15Args) | |||
| 27 | - | }} | |||
| 28 | - | ||||
| 29 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | 24 | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | ||
| 30 | // It panics if it can't find the name symbol. | 25 | // It panics if it can't find the name symbol. | ||
| 31 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | 26 | func RegisterLibFunc(fptr any, handle uintptr, name string) { | ||
| @@ -310,8 +305,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 310 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | 305 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | ||
| 311 | } | 306 | } | ||
| 312 | 307 | ||||
| 313 | - | syscall := thePool.Get().(*syscall15Args) | 308 | + | syscall := &syscall15Args{} |
| 314 | - | defer thePool.Put(syscall) | |||
| 315 | 309 | ||||
| 316 | if runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" { | 310 | if runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" { | ||
| 317 | syscall.Set(cfn, sysargs[:], floats[:], 0) | 311 | syscall.Set(cfn, sysargs[:], floats[:], 0) | ||
| @@ -321,7 +315,6 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 321 | syscall.Set(cfn, sysargs[:], floats[:], arm64_r8) | 315 | syscall.Set(cfn, sysargs[:], floats[:], arm64_r8) | ||
| 322 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall)) | 316 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall)) | ||
| 323 | } else { | 317 | } else { | ||
| 324 | - | *syscall = syscall15Args{} | |||
| 325 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | 318 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | ||
| 326 | syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | 319 | syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | ||
| 327 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | 320 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | ||
M func_test.go +35 -0
| @@ -11,6 +11,7 @@ import ( | |||
| 11 | 11 | "path/filepath" | |
| 12 | 12 | "runtime" | |
| 13 | 13 | "strings" | |
| 14 | + | "sync" | |
| 14 | 15 | "testing" | |
| 15 | 16 | "unsafe" | |
| 16 | 17 | ||
| @@ -35,6 +36,40 @@ func getSystemLibrary() (string, error) { | |||
| 35 | 36 | } | |
| 36 | 37 | } | |
| 37 | 38 | ||
| 39 | + | func TestRegisterFunc_ConcurrentPointerReturn(t *testing.T) { | |
| 40 | + | library, err := getSystemLibrary() | |
| 41 | + | if err != nil { | |
| 42 | + | t.Fatalf("couldn't get system library: %s", err) | |
| 43 | + | } | |
| 44 | + | libc, err := load.OpenLibrary(library) | |
| 45 | + | if err != nil { | |
| 46 | + | t.Fatalf("failed to dlopen: %s", err) | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var alloc func(uint64) *byte | |
| 50 | + | var free func(*byte) | |
| 51 | + | purego.RegisterLibFunc(&alloc, libc, "malloc") | |
| 52 | + | purego.RegisterLibFunc(&free, libc, "free") | |
| 53 | + | ||
| 54 | + | var wg sync.WaitGroup | |
| 55 | + | ||
| 56 | + | for i := 0; i < runtime.NumCPU(); i++ { | |
| 57 | + | wg.Add(1) | |
| 58 | + | go func(id int) { | |
| 59 | + | defer wg.Done() | |
| 60 | + | for j := 0; j < 400_000; j++ { | |
| 61 | + | ptr := alloc(5) | |
| 62 | + | if ptr == nil { | |
| 63 | + | continue | |
| 64 | + | } | |
| 65 | + | free(ptr) | |
| 66 | + | } | |
| 67 | + | }(i) | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | wg.Wait() | |
| 71 | + | } | |
| 72 | + | ||
| 38 | 73 | func TestRegisterFunc(t *testing.T) { | |
| 39 | 74 | library, err := getSystemLibrary() | |
| 40 | 75 | if err != nil { | |
| @@ -11,6 +11,7 @@ import ( | |||||
| 11 | "path/filepath" | 11 | "path/filepath" | ||
| 12 | "runtime" | 12 | "runtime" | ||
| 13 | "strings" | 13 | "strings" | ||
| 14 | + | "sync" | |||
| 14 | "testing" | 15 | "testing" | ||
| 15 | "unsafe" | 16 | "unsafe" | ||
| 16 | 17 | ||||
| @@ -35,6 +36,40 @@ func getSystemLibrary() (string, error) { | |||||
| 35 | } | 36 | } | ||
| 36 | } | 37 | } | ||
| 37 | 38 | ||||
| 39 | + | func TestRegisterFunc_ConcurrentPointerReturn(t *testing.T) { | |||
| 40 | + | library, err := getSystemLibrary() | |||
| 41 | + | if err != nil { | |||
| 42 | + | t.Fatalf("couldn't get system library: %s", err) | |||
| 43 | + | } | |||
| 44 | + | libc, err := load.OpenLibrary(library) | |||
| 45 | + | if err != nil { | |||
| 46 | + | t.Fatalf("failed to dlopen: %s", err) | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | var alloc func(uint64) *byte | |||
| 50 | + | var free func(*byte) | |||
| 51 | + | purego.RegisterLibFunc(&alloc, libc, "malloc") | |||
| 52 | + | purego.RegisterLibFunc(&free, libc, "free") | |||
| 53 | + | ||||
| 54 | + | var wg sync.WaitGroup | |||
| 55 | + | ||||
| 56 | + | for i := 0; i < runtime.NumCPU(); i++ { | |||
| 57 | + | wg.Add(1) | |||
| 58 | + | go func(id int) { | |||
| 59 | + | defer wg.Done() | |||
| 60 | + | for j := 0; j < 400_000; j++ { | |||
| 61 | + | ptr := alloc(5) | |||
| 62 | + | if ptr == nil { | |||
| 63 | + | continue | |||
| 64 | + | } | |||
| 65 | + | free(ptr) | |||
| 66 | + | } | |||
| 67 | + | }(i) | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | wg.Wait() | |||
| 71 | + | } | |||
| 72 | + | ||||
| 38 | func TestRegisterFunc(t *testing.T) { | 73 | func TestRegisterFunc(t *testing.T) { | ||
| 39 | library, err := getSystemLibrary() | 74 | library, err := getSystemLibrary() | ||
| 40 | if err != nil { | 75 | if err != nil { | ||
M syscall_sysv.go +1 -4
| @@ -16,10 +16,7 @@ import ( | |||
| 16 | 16 | var syscall15XABI0 uintptr | |
| 17 | 17 | ||
| 18 | 18 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | |
| 19 | - | args := thePool.Get().(*syscall15Args) | |
| 20 | - | defer thePool.Put(args) | |
| 21 | - | ||
| 22 | - | *args = syscall15Args{ | |
| 19 | + | args := &syscall15Args{ | |
| 23 | 20 | fn: fn, | |
| 24 | 21 | a1: a1, a2: a2, a3: a3, a4: a4, a5: a5, a6: a6, a7: a7, a8: a8, | |
| 25 | 22 | a9: a9, a10: a10, a11: a11, a12: a12, a13: a13, a14: a14, a15: a15, | |
| @@ -16,10 +16,7 @@ import ( | |||||
| 16 | var syscall15XABI0 uintptr | 16 | var syscall15XABI0 uintptr | ||
| 17 | 17 | ||||
| 18 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | 18 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | ||
| 19 | - | args := thePool.Get().(*syscall15Args) | 19 | + | args := &syscall15Args{ |
| 20 | - | defer thePool.Put(args) | |||
| 21 | - | ||||
| 22 | - | *args = syscall15Args{ | |||
| 23 | fn: fn, | 20 | fn: fn, | ||
| 24 | a1: a1, a2: a2, a3: a3, a4: a4, a5: a5, a6: a6, a7: a7, a8: a8, | 21 | a1: a1, a2: a2, a3: a3, a4: a4, a5: a5, a6: a6, a7: a7, a8: a8, | ||
| 25 | a9: a9, a10: a10, a11: a11, a12: a12, a13: a13, a14: a14, a15: a15, | 22 | a9: a9, a10: a10, a11: a11, a12: a12, a13: a13, a14: a14, a15: a15, | ||