fix(wgpu): handle callback string views across ABIs#25
Conversation
34ed1d3 to
1f62a0e
Compare
kolkov
left a comment
There was a problem hiding this comment.
Excellent work — the ABI split between flat-register and indirect-aggregate callback conventions is the right architectural approach, and the root cause analysis of how WGPUStringView inline passing shifts userdata1 is spot-on. The refactoring from 4 duplicate StringView extraction blocks into shared handle*Callback functions with platform-specific entry points is clean and well-structured.
That said, there are a few things to address before merge:
1. "Closes #17" — confirming the two-part fix
For context: our earlier research (MACOS_CALLBACK_HANG.md, 2026-03-20) identified re-entrant callback invocation through crosscall2 as the primary macOS hang cause. That was fixed in goffi v0.4.0 (crosscall2 integration, #16). With goffi v0.6.0 (our current minimum), callbacks from synchronous C→Go transitions work correctly.
That leaves the ABI mismatch this PR fixes as the remaining piece — and your macOS arm64 verification confirms it. Could you add a brief note to the PR description that the full fix requires goffi ≥ v0.4.0 (crosscall2 routing) + this PR (StringView ABI normalization)? go.mod already enforces v0.6.0, so this is just for future readers understanding the two-part fix.
2. Windows x64 path has zero test coverage (mandatory)
callback_flat_test.go has build tag ((linux || darwin || freebsd) && (amd64 || arm64)) || (windows && arm64) — which excludes Windows x64. The callbackStringView function in callback_windows_amd64.go (the indirect dereference path) has no test.
Please add a callback_windows_amd64_test.go with //go:build windows && amd64 that tests at minimum:
callbackStringView(0)returns emptyStringViewcallbackStringView(validPtr)returns correctData/Length- One full round-trip through
adapterCallbackEntry(same pattern as the flat test)
3. Build tag gap for uncommon platforms
The two files cover:
callback_flat.go: ((linux|darwin|freebsd) && (amd64|arm64)) || (windows && arm64)
callback_windows_amd64.go: windows && amd64
Platforms like linux/386, linux/riscv64, freebsd/386 have no *Entry functions defined → compile failure. These aren't current targets, but a stub file with a clear build error would be better than a cryptic "undefined: adapterCallbackEntry":
A comment at the top of callback_flat.go documenting the supported platform matrix would be sufficient, since 386/riscv64 aren't wgpu-native targets anyway. Something like:
// Supported callback platforms: amd64 and arm64 on Linux, macOS, FreeBSD, Windows.
// Other architectures (386, riscv64, etc.) are not supported by wgpu-native.4. Missing edge case tests
The TestABICallbackEntriesPreserveStringViewAndUserdata test covers the happy path well. Please add:
- Null/empty message:
messageData=0, messageLength=0→ verifyreq.message == "" - Unknown userdata1: call entry with unregistered request ID → verify no panic, returns normally
- Zero-length with non-null data:
messageData=somePtr, messageLength=0→ verify empty string
5. Minor: document userdata2 drop
Old handlers accepted userdata2 and ignored it. New shared handlers drop it entirely (the entry functions use _). A one-line comment in one of the handle*Callback functions would help:
// userdata2 is reserved by the WebGPU spec and unused.Summary
The ABI analysis is correct and well-executed:
| Platform | C ABI for WGPUStringView (16B) | Entry function | Status |
|---|---|---|---|
| Unix amd64/arm64 | Two integer registers | callback_flat.go (reconstruct from 2 args) |
✅ Correct |
| Windows ARM64 | Two GP registers (AAPCS64) | callback_flat.go (same as Unix) |
✅ Correct |
| Windows x64 | Indirect (pointer in RCX) | callback_windows_amd64.go (dereference) |
✅ Correct, needs test |
The code quality is high — the shared/entry split eliminates duplication, build tags are correctly partitioned, and the 109-line test is thorough. With the Windows x64 test and the #17 mechanism clarification, this is ready to merge.
5e08b18 to
b77b002
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
8f71da9 to
9006aca
Compare
kolkov
left a comment
There was a problem hiding this comment.
All five review items addressed. Windows x64 test coverage added, edge cases covered, platform matrix documented, userdata2 drop noted. ABI analysis verified, CI green on all platforms. LGTM.
Summary
WGPUStringViewcallback arguments at an ABI-specific private seamRoot cause
wgpu-native passes callback messages as an inline two-word
WGPUStringView. Unix amd64/arm64 and Windows ARM64 place those words in two integer registers, but the Go callback handlers consumed them as one pointer argument. That shifteduserdata1onto the message length (commonly zero), so the registered request was never completed andRequestAdapterwaited forever.Windows x64 instead passes the aggregate indirectly. Platform entries now normalize both representations into the same shared completion logic.
The complete fix is intentionally two-part: goffi v0.4.0 added the
crosscall2routing required for re-entrant C-to-Go callbacks, and this PR normalizes the remainingWGPUStringViewABI mismatch. This module already requires goffi v0.6.0, so consumers receive both pieces.Closes #17.
Verification
CGO_ENABLED=0, and wgpu-native v29.0.0.0TestRequestAdapterreturns a valid Metal adapter after the fixTestABICI filterCGO_ENABLED=0 go vet ./wgpu/...git diff --checkThe unrestricted macOS GPU suite now advances beyond adapter creation but encounters an existing wgpu-native abort in untouched bind-group-layout code; the callback-specific paths above pass.