Skip to content

Keep packed acquisition arguments and callbacks alive across native calls - #10

Open
dylanebert wants to merge 3 commits into
kommander:mainfrom
dylanebert:upstream/keep-acquisition-alive
Open

dylanebert wants to merge 3 commits into
kommander:mainfrom
dylanebert:upstream/keep-acquisition-alive

Conversation

@dylanebert

Copy link
Copy Markdown

Supersedes #9, which closed when its source branch was deleted. Same change, now on a branch that stays.

Problem

On bun-webgpu 0.1.7 (Bun 1.4.x), twenty requestDevice calls with Bun.gc(true) forced between descriptor packing and wgpuAdapterRequestDevice produce descriptor drift: devices come back with features and limits that differ from the request, or acquisition fails outright.

Cause

WGPUDeviceDescriptorStruct.pack (and every nested struct, string, enum/struct/primitive array and object-pointer array it packs) allocates ArrayBuffers that are only reachable through raw pointers written into the parent buffer. Nothing in JS holds them, so a collection before or during the native call frees memory Dawn is about to read. The same applies to requestAdapter options, the callback-info structs, queue.onSubmittedWorkDone and device.popErrorScope callback info (the pop-scope userdata buffer included). The device's uncaptured-error and device-lost JSCallbacks were locals too, even though native code calls them for the device's lifetime.

Fix

  • structs_ffi.ts: CallArena, passed as a new arena pack option; every buffer a pack allocates (and every object whose .ptr it writes) joins it.
  • requestAdapter / requestDevice: one arena per request, released after the request callback runs (or on failure).
  • GPUAdapter: keeps the device error/loss callbacks referenced.
  • GPUQueue.onSubmittedWorkDone, GPUDevice.popErrorScope: keep the callback info (and userdata) referenced until completion.

Evidence

src/acquisition.test.ts: twenty acquisitions with a forced GC plus allocation churn wedged in front of the native call; each device's features and every limit must equal the request. Passes with the fix (800 expects, RTX 4090 / Vulkan, Bun 1.4.2); the existing suite is unchanged (186 pass, 1 skip).

🤖 Generated with Claude Code

https://claude.ai/code/session_01FFh77i2dpmMjbVhbEUd5qX

* bridge: keep packed acquisition arguments and callbacks alive

A CallArena holds every buffer a packed descriptor points into until the request callback runs; device error/loss callbacks live with the adapter; queue and pop-error-scope callback info stay referenced until completion. Version 0.1.7-shallot.1 with platform packages as optional dependencies so a git install finds its native library.

* bridge: lock platform packages

* bridge: link upstream PR in README
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.

1 participant