Skip to content

fix(wgpu): handle callback string views across ABIs#25

Merged
kolkov merged 3 commits into
go-webgpu:mainfrom
besmpl:besmpl/fix-macos-callback-abi
Jul 24, 2026
Merged

fix(wgpu): handle callback string views across ABIs#25
kolkov merged 3 commits into
go-webgpu:mainfrom
besmpl:besmpl/fix-macos-callback-abi

Conversation

@besmpl

@besmpl besmpl commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • normalize WGPUStringView callback arguments at an ABI-specific private seam
  • preserve callback userdata on Unix amd64/arm64 and Windows ARM64
  • retain Windows x64's indirect aggregate convention
  • add a no-GPU ABI regression test covering all four callback families in the standard CI lane

Root 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 shifted userdata1 onto the message length (commonly zero), so the registered request was never completed and RequestAdapter waited 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 crosscall2 routing required for re-entrant C-to-Go callbacks, and this PR normalizes the remaining WGPUStringView ABI mismatch. This module already requires goffi v0.6.0, so consumers receive both pieces.

Closes #17.

Verification

  • reproduced the pre-fix timeout on macOS arm64 with Go 1.26.4, CGO_ENABLED=0, and wgpu-native v29.0.0.0
  • verified TestRequestAdapter returns a valid Metal adapter after the fix
  • passed focused adapter, device, compute/map, and error-scope GPU tests
  • passed the no-GPU callback ABI regression test through the repository's TestABI CI filter
  • confirmed the Windows ARM64 callback parameter registers with a targeted Clang ABI assembly observer
  • passed Linux amd64/arm64 and Windows amd64/arm64 cross-builds
  • passed CGO_ENABLED=0 go vet ./wgpu/...
  • passed golangci-lint v2
  • passed formatting and git diff --check

The 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.

@besmpl
besmpl marked this pull request as ready for review July 19, 2026 11:54
@besmpl
besmpl requested a review from kolkov as a code owner July 19, 2026 11:54
@besmpl
besmpl force-pushed the besmpl/fix-macos-callback-abi branch from 34ed1d3 to 1f62a0e Compare July 19, 2026 12:09
@besmpl besmpl changed the title fix(wgpu): handle callback string views on Unix fix(wgpu): handle callback string views across ABIs Jul 19, 2026

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 empty StringView
  • callbackStringView(validPtr) returns correct Data/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 → verify req.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.

@besmpl
besmpl force-pushed the besmpl/fix-macos-callback-abi branch from 5e08b18 to b77b002 Compare July 22, 2026 16:32
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@besmpl
besmpl force-pushed the besmpl/fix-macos-callback-abi branch from 8f71da9 to 9006aca Compare July 23, 2026 18:28

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@kolkov
kolkov merged commit 32ee3f5 into go-webgpu:main Jul 24, 2026
7 checks passed
@kolkov kolkov mentioned this pull request Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RequestAdapter blocks forever on macos

2 participants