purego: remove pooling of syscall (#452)
- commit
- 11272d2804cdba6e4e4e6635e80f035a14f9ccbe
- author
- TotallyGamerJet · · 2026-05-24 12:47 UTC
- committer
- GitHub · · 2026-05-24 12:47 UTC
- parent
- f8849c8
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: v0.11.0-alpha.3
5 files · +39 -16
M func.go +1 -7
| @@ -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(syscallArgs) | |
| 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) { | |
| @@ -323,13 +318,12 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 323 | 318 | var syscall *syscallArgs | |
| 324 | 319 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | |
| 325 | 320 | // Windows amd64, 386, and arm use syscall.SyscallN. | |
| 326 | - | syscall = thePool.Get().(*syscallArgs) | |
| 321 | + | syscall = &syscallArgs{} | |
| 327 | 322 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | |
| 328 | 323 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | |
| 329 | 324 | } else { | |
| 330 | 325 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | |
| 331 | 326 | } | |
| 332 | - | defer thePool.Put(syscall) | |
| 333 | 327 | if ty.NumOut() == 0 { | |
| 334 | 328 | return nil | |
| 335 | 329 | } | |
| @@ -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(syscallArgs) | |||
| 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) { | ||
| @@ -323,13 +318,12 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 323 | var syscall *syscallArgs | 318 | var syscall *syscallArgs | ||
| 324 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | 319 | if runtime.GOOS == "windows" && runtime.GOARCH != "arm64" { | ||
| 325 | // Windows amd64, 386, and arm use syscall.SyscallN. | 320 | // Windows amd64, 386, and arm use syscall.SyscallN. | ||
| 326 | - | syscall = thePool.Get().(*syscallArgs) | 321 | + | syscall = &syscallArgs{} |
| 327 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | 322 | syscall.a1, syscall.a2, _ = syscall_syscallN(cfn, sysargs[:numStack]...) | ||
| 328 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | 323 | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | ||
| 329 | } else { | 324 | } else { | ||
| 330 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | 325 | syscall = syscall_SyscallN(cfn, sysargs[:], floats[:], arm64_r8) | ||
| 331 | } | 326 | } | ||
| 332 | - | defer thePool.Put(syscall) | |||
| 333 | if ty.NumOut() == 0 { | 327 | if ty.NumOut() == 0 { | ||
| 334 | return nil | 328 | return nil | ||
| 335 | } | 329 | } | ||
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.go +1 -3
| @@ -22,8 +22,7 @@ type syscallArgs struct { | |||
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 25 | - | s := thePool.Get().(*syscallArgs) | |
| 26 | - | *s = syscallArgs{ | |
| 25 | + | s := &syscallArgs{ | |
| 27 | 26 | fn: fn, | |
| 28 | 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 29 | 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | |
| @@ -79,6 +78,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 79 | 78 | var floats [maxArgs]uintptr | |
| 80 | 79 | copy(floats[:], tmp[:]) | |
| 81 | 80 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 82 | - | defer thePool.Put(s) | |
| 83 | 81 | return s.a1, s.a2, s.a3 | |
| 84 | 82 | } | |
| @@ -22,8 +22,7 @@ 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 := thePool.Get().(*syscallArgs) | 25 | + | s := &syscallArgs{ |
| 26 | - | *s = syscallArgs{ | |||
| 27 | fn: fn, | 26 | fn: fn, | ||
| 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 29 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | ||
| @@ -79,6 +78,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||||
| 79 | var floats [maxArgs]uintptr | 78 | var floats [maxArgs]uintptr | ||
| 80 | copy(floats[:], tmp[:]) | 79 | copy(floats[:], tmp[:]) | ||
| 81 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 80 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 82 | - | defer thePool.Put(s) | |||
| 83 | return s.a1, s.a2, s.a3 | 81 | return s.a1, s.a2, s.a3 | ||
| 84 | } | 82 | } | ||
M syscall_32bit.go +1 -3
| @@ -22,8 +22,7 @@ type syscallArgs struct { | |||
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 25 | - | s := thePool.Get().(*syscallArgs) | |
| 26 | - | *s = syscallArgs{ | |
| 25 | + | s := &syscallArgs{ | |
| 27 | 26 | fn: fn, | |
| 28 | 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 29 | 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | |
| @@ -81,6 +80,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 81 | 80 | var floats [16]uintptr | |
| 82 | 81 | copy(floats[:], tmp[:16]) | |
| 83 | 82 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 84 | - | defer thePool.Put(s) | |
| 85 | 83 | return s.a1, s.a2, s.a3 | |
| 86 | 84 | } | |
| @@ -22,8 +22,7 @@ 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 := thePool.Get().(*syscallArgs) | 25 | + | s := &syscallArgs{ |
| 26 | - | *s = syscallArgs{ | |||
| 27 | fn: fn, | 26 | fn: fn, | ||
| 28 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 27 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 29 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 28 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | ||
| @@ -81,6 +80,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||||
| 81 | var floats [16]uintptr | 80 | var floats [16]uintptr | ||
| 82 | copy(floats[:], tmp[:16]) | 81 | copy(floats[:], tmp[:16]) | ||
| 83 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 82 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 84 | - | defer thePool.Put(s) | |||
| 85 | return s.a1, s.a2, s.a3 | 83 | return s.a1, s.a2, s.a3 | ||
| 86 | } | 84 | } | ||
M syscall_ppc64le.go +1 -3
| @@ -20,8 +20,7 @@ type syscallArgs struct { | |||
| 20 | 20 | } | |
| 21 | 21 | ||
| 22 | 22 | func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs { | |
| 23 | - | s := thePool.Get().(*syscallArgs) | |
| 24 | - | *s = syscallArgs{ | |
| 23 | + | s := &syscallArgs{ | |
| 25 | 24 | fn: fn, | |
| 26 | 25 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | |
| 27 | 26 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | |
| @@ -67,6 +66,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 67 | 66 | var floats [maxArgs]uintptr | |
| 68 | 67 | copy(floats[:], tmp[:]) | |
| 69 | 68 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | |
| 70 | - | defer thePool.Put(s) | |
| 71 | 69 | return s.a1, s.a2, s.a3 | |
| 72 | 70 | } | |
| @@ -20,8 +20,7 @@ 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 := thePool.Get().(*syscallArgs) | 23 | + | s := &syscallArgs{ |
| 24 | - | *s = syscallArgs{ | |||
| 25 | fn: fn, | 24 | fn: fn, | ||
| 26 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | 25 | a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3], | ||
| 27 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | 26 | a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7], | ||
| @@ -67,6 +66,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||||
| 67 | var floats [maxArgs]uintptr | 66 | var floats [maxArgs]uintptr | ||
| 68 | copy(floats[:], tmp[:]) | 67 | copy(floats[:], tmp[:]) | ||
| 69 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | 68 | s := syscall_SyscallN(fn, tmp[:], floats[:], 0) | ||
| 70 | - | defer thePool.Put(s) | |||
| 71 | return s.a1, s.a2, s.a3 | 69 | return s.a1, s.a2, s.a3 | ||
| 72 | } | 70 | } | ||