detached 6f1d5dd

perf(server): dispatch C callbacks without reflection

commit
6f1d5dd52f140299f7733392fb5468e39679aeab
author
brice · · 2026-09-27 18:04 UTC
committer
brice · · 2026-09-27 18:47 UTC
parent
a10562a
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +23 -22
M server/lib.go +23 -22
@@ -83,47 +83,48 @@ func load() error {
8383 }
8484 }
8585
86- cbDispatcher = purego.NewCallback(dispatch)
87- cbDestroy = purego.NewCallback(destroyed)
88- cbBind = purego.NewCallback(bind)
89- cbWake = purego.NewCallback(wake)
86+ cbDispatcher = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
87+ // C memory owned by libwayland; reinterpret pointer bits without uintptr-to-pointer conversion.
88+ msg, args := a.Int(3), a.Int(4)
89+ return uintptr(uint32(dispatch(a.Int(0), a.Int(1), uint32(a.Int(2)), *(*unsafe.Pointer)(unsafe.Pointer(&msg)), *(*unsafe.Pointer)(unsafe.Pointer(&args)))))
90+ })
91+ cbDestroy = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
92+ destroyed(a.Int(0))
93+ return 0
94+ })
95+ cbBind = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
96+ bind(a.Int(0), a.Int(1), uint32(a.Int(2)), uint32(a.Int(3)))
97+ return 0
98+ })
99+ cbWake = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
100+ return uintptr(uint32(wake(int32(a.Int(0)), uint32(a.Int(1)), a.Int(2))))
101+ })
90102 })
91103 return loadErr
92104 }
93105
94-// callArgs is scratch for SyscallN so hot calls do not allocate a variadic
95-// slice. Display goroutine only; all libwayland calls run there.
96-var callArgs [6]uintptr
97-
98106 // Integer and pointer-only entry points avoid RegisterLibFunc's reflective call path.
99107 func wlDisplayFlushClients(display uintptr) {
100- callArgs[0] = display
101- purego.SyscallN(symDisplayFlushClients, callArgs[:1]...)
108+ purego.Syscall6(symDisplayFlushClients, display, 0, 0, 0, 0, 0)
102109 }
103110 func wlEventLoopDispatch(loop uintptr, timeout int32) int32 {
104- callArgs[0], callArgs[1] = loop, uintptr(timeout)
105- r, _, _ := purego.SyscallN(symEventLoopDispatch, callArgs[:2]...)
111+ r, _, _ := purego.Syscall6(symEventLoopDispatch, loop, uintptr(timeout), 0, 0, 0, 0)
106112 return int32(r)
107113 }
108114 func wlResourceCreate(client, iface uintptr, version int32, id uint32) uintptr {
109- callArgs[0], callArgs[1], callArgs[2], callArgs[3] = client, iface, uintptr(version), uintptr(id)
110- r, _, _ := purego.SyscallN(symResourceCreate, callArgs[:4]...)
115+ r, _, _ := purego.Syscall6(symResourceCreate, client, iface, uintptr(version), uintptr(id), 0, 0)
111116 return r
112117 }
113118 func wlResourceGetID(resource uintptr) uint32 {
114- callArgs[0] = resource
115- r, _, _ := purego.SyscallN(symResourceGetID, callArgs[:1]...)
119+ r, _, _ := purego.Syscall6(symResourceGetID, resource, 0, 0, 0, 0, 0)
116120 return uint32(r)
117121 }
118122 func wlResourceSetDispatcher(resource, dispatcher, impl, data, destroy uintptr) {
119- callArgs[0], callArgs[1], callArgs[2], callArgs[3], callArgs[4] = resource, dispatcher, impl, data, destroy
120- purego.SyscallN(symResourceSetDispatcher, callArgs[:5]...)
123+ purego.Syscall6(symResourceSetDispatcher, resource, dispatcher, impl, data, destroy, 0)
121124 }
122125 func wlResourcePostEventArr(resource uintptr, opcode uint32, args unsafe.Pointer) {
123- callArgs[0], callArgs[1], callArgs[2] = resource, uintptr(opcode), uintptr(args)
124- purego.SyscallN(symResourcePostEventArr, callArgs[:3]...)
126+ purego.Syscall6(symResourcePostEventArr, resource, uintptr(opcode), uintptr(args), 0, 0, 0)
125127 }
126128 func wlResourceDestroy(resource uintptr) {
127- callArgs[0] = resource
128- purego.SyscallN(symResourceDestroy, callArgs[:1]...)
129+ purego.Syscall6(symResourceDestroy, resource, 0, 0, 0, 0, 0)
129130 }
@@ -83,47 +83,48 @@ func load() error {
83 }83 }
84 }84 }
85 85
86- cbDispatcher = purego.NewCallback(dispatch)86+ cbDispatcher = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
87- cbDestroy = purego.NewCallback(destroyed)87+ // C memory owned by libwayland; reinterpret pointer bits without uintptr-to-pointer conversion.
88- cbBind = purego.NewCallback(bind)88+ msg, args := a.Int(3), a.Int(4)
89- cbWake = purego.NewCallback(wake)89+ return uintptr(uint32(dispatch(a.Int(0), a.Int(1), uint32(a.Int(2)), *(*unsafe.Pointer)(unsafe.Pointer(&msg)), *(*unsafe.Pointer)(unsafe.Pointer(&args)))))
90+ })
91+ cbDestroy = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
92+ destroyed(a.Int(0))
93+ return 0
94+ })
95+ cbBind = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
96+ bind(a.Int(0), a.Int(1), uint32(a.Int(2)), uint32(a.Int(3)))
97+ return 0
98+ })
99+ cbWake = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
100+ return uintptr(uint32(wake(int32(a.Int(0)), uint32(a.Int(1)), a.Int(2))))
101+ })
90 })102 })
91 return loadErr103 return loadErr
92 }104 }
93 105
94-// callArgs is scratch for SyscallN so hot calls do not allocate a variadic
95-// slice. Display goroutine only; all libwayland calls run there.
96-var callArgs [6]uintptr
97-
98 // Integer and pointer-only entry points avoid RegisterLibFunc's reflective call path.106 // Integer and pointer-only entry points avoid RegisterLibFunc's reflective call path.
99 func wlDisplayFlushClients(display uintptr) {107 func wlDisplayFlushClients(display uintptr) {
100- callArgs[0] = display108+ purego.Syscall6(symDisplayFlushClients, display, 0, 0, 0, 0, 0)
101- purego.SyscallN(symDisplayFlushClients, callArgs[:1]...)
102 }109 }
103 func wlEventLoopDispatch(loop uintptr, timeout int32) int32 {110 func wlEventLoopDispatch(loop uintptr, timeout int32) int32 {
104- callArgs[0], callArgs[1] = loop, uintptr(timeout)111+ r, _, _ := purego.Syscall6(symEventLoopDispatch, loop, uintptr(timeout), 0, 0, 0, 0)
105- r, _, _ := purego.SyscallN(symEventLoopDispatch, callArgs[:2]...)
106 return int32(r)112 return int32(r)
107 }113 }
108 func wlResourceCreate(client, iface uintptr, version int32, id uint32) uintptr {114 func wlResourceCreate(client, iface uintptr, version int32, id uint32) uintptr {
109- callArgs[0], callArgs[1], callArgs[2], callArgs[3] = client, iface, uintptr(version), uintptr(id)115+ r, _, _ := purego.Syscall6(symResourceCreate, client, iface, uintptr(version), uintptr(id), 0, 0)
110- r, _, _ := purego.SyscallN(symResourceCreate, callArgs[:4]...)
111 return r116 return r
112 }117 }
113 func wlResourceGetID(resource uintptr) uint32 {118 func wlResourceGetID(resource uintptr) uint32 {
114- callArgs[0] = resource119+ r, _, _ := purego.Syscall6(symResourceGetID, resource, 0, 0, 0, 0, 0)
115- r, _, _ := purego.SyscallN(symResourceGetID, callArgs[:1]...)
116 return uint32(r)120 return uint32(r)
117 }121 }
118 func wlResourceSetDispatcher(resource, dispatcher, impl, data, destroy uintptr) {122 func wlResourceSetDispatcher(resource, dispatcher, impl, data, destroy uintptr) {
119- callArgs[0], callArgs[1], callArgs[2], callArgs[3], callArgs[4] = resource, dispatcher, impl, data, destroy123+ purego.Syscall6(symResourceSetDispatcher, resource, dispatcher, impl, data, destroy, 0)
120- purego.SyscallN(symResourceSetDispatcher, callArgs[:5]...)
121 }124 }
122 func wlResourcePostEventArr(resource uintptr, opcode uint32, args unsafe.Pointer) {125 func wlResourcePostEventArr(resource uintptr, opcode uint32, args unsafe.Pointer) {
123- callArgs[0], callArgs[1], callArgs[2] = resource, uintptr(opcode), uintptr(args)126+ purego.Syscall6(symResourcePostEventArr, resource, uintptr(opcode), uintptr(args), 0, 0, 0)
124- purego.SyscallN(symResourcePostEventArr, callArgs[:3]...)
125 }127 }
126 func wlResourceDestroy(resource uintptr) {128 func wlResourceDestroy(resource uintptr) {
127- callArgs[0] = resource129+ purego.Syscall6(symResourceDestroy, resource, 0, 0, 0, 0, 0)
128- purego.SyscallN(symResourceDestroy, callArgs[:1]...)
129 }130 }

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 14

↑↓ moveenter openesc close