detached eaff005

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 UnverifiedB5690EEEBB952194 The 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 (
1010 "math"
1111 "reflect"
1212 "runtime"
13- "sync"
1413 "unsafe"
1514
1615 "github.com/ebitengine/purego/internal/strings"
@@ -22,10 +21,6 @@ const (
2221 align8ByteSize = 8 // 8-byte alignment boundary
2322 )
2423
25-var thePool = sync.Pool{New: func() any {
26- return new(syscall15Args)
27-}}
28-
2924 // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
3025 // It panics if it can't find the name symbol.
3126 func RegisterLibFunc(fptr any, handle uintptr, name string) {
@@ -310,8 +305,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
310305 keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
311306 }
312307
313- syscall := thePool.Get().(*syscall15Args)
314- defer thePool.Put(syscall)
308+ syscall := &syscall15Args{}
315309
316310 if runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x" {
317311 syscall.Set(cfn, sysargs[:], floats[:], 0)
@@ -321,7 +315,6 @@ func RegisterFunc(fptr any, cfn uintptr) {
321315 syscall.Set(cfn, sysargs[:], floats[:], arm64_r8)
322316 runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
323317 } else {
324- *syscall = syscall15Args{}
325318 // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
326319 syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
327320 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 boundary21 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 floats318 // 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 (
1111 "path/filepath"
1212 "runtime"
1313 "strings"
14+ "sync"
1415 "testing"
1516 "unsafe"
1617
@@ -35,6 +36,40 @@ func getSystemLibrary() (string, error) {
3536 }
3637 }
3738
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+
3873 func TestRegisterFunc(t *testing.T) {
3974 library, err := getSystemLibrary()
4075 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 (
1616 var syscall15XABI0 uintptr
1717
1818 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{
2320 fn: fn,
2421 a1: a1, a2: a2, a3: a3, a4: a4, a5: a5, a6: a6, a7: a7, a8: a8,
2522 a9: a9, a10: a10, a11: a11, a12: a12, a13: a13, a14: a14, a15: a15,
@@ -16,10 +16,7 @@ import (
16 var syscall15XABI0 uintptr16 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,

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close