fix(device): drain GPU work before teardown#264
Conversation
|
Stacked consumer preview: #268 at exact head The preview remains explicitly draft/non-mergeable and will be rebased to Android-only commits after this prerequisite lands. |
0ed1706 to
6acfb1e
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
kolkov
left a comment
There was a problem hiding this comment.
Good catch. The released flag was short-circuiting WaitIdle() during Release(), meaning the GPU was never actually drained before staging buffers and encoders were destroyed — a real use-after-free risk on all backends, though most drivers mask it with internal refcounting.
The private waitIdle() approach is clean: preserves the public API guard while allowing teardown to drain properly. The reorder of queue.release() to after waitIdle() matches Rust wgpu's Queue::Drop ordering where pending_writes is dropped after the fence wait completes (queue.rs:53, 157-251).
The lifecycle test with noop backend embedding is a solid way to assert the [wait-idle, destroy] invariant without requiring real GPU hardware.
LGTM.
Summary
Make
Device.Releasewait for the HAL device to become idle before releasing pending writes, flushing deferred destruction, destroying pooled encoders, and finally destroying the core/HAL device.The public
WaitIdlemethod still rejects calls after release. Teardown uses a privatewaitIdlepath becauseReleasemarks the device unavailable first, preventing concurrent new work while the final submission is drained.Why
The previous order set the released bit and then called the public
WaitIdle, which immediately returnedErrReleased. Deferred encoder-recycling callbacks could therefore remain queued while HAL destruction reclaimed the same command pools.Validation
go test ./...go test -race .WaitIdlehappens before HALDestroyThis is intentionally separate from the Android work; it fixes native teardown ordering on every backend.
Android integration relationship
This Android-independent change is one of five prerequisites replayed in #268. Its canonical candidate head is
6acfb1ea1cca1dc14d7ab0902931c678dc13e0a6; the corresponding replay in #268 ends atb2842f068d80d22eae297059aa983965965a9151. The #268 description records the current exact integration head and full replay map; it drops this replay after this PR merges. Rust-v29 typed surface parity remains separate in #273. Android runtime and release claims remain in #268.