fix(webgpu): own mapped range backing stores#36257
Conversation
bartlomieju
left a comment
There was a problem hiding this comment.
LGTM. This correctly closes the dangling-pointer hazard by moving every returned range into V8-owned storage and writing writable ranges back before the mapping closes. I verified the writeback against wgpu-core get_mapped_range (it recomputes the pointer from the live mapping each call and keeps no per-range bookkeeping, so re-fetching during writeback is idempotent), the copy_nonoverlapping bounds, and mappedAtCreation (map_mode is Write, so it flushes via the Init staging arm). Nice multi-range and detach test coverage. Two minor, non-blocking notes inline.
| let ab = ab.open(scope); | ||
| ab.detach(None); | ||
| } | ||
| self.writeback_and_detach_mapped_js_buffers(scope)?; |
There was a problem hiding this comment.
Minor: this early-returns before buffer_unmap, so if writeback ever errors the buffer stays mapped with map_state == "mapped". In practice this is unreachable (the range was already validated at getMappedRange time and the mapping is still active, and the old buffer_unmap()? would have errored in the same invalidated cases), so there's no observable regression. A one-line comment noting the intentional ordering would help future readers.
| let mut first_error = None; | ||
| for mapped in self.mapped_js_buffers.replace(vec![]) { | ||
| let ab = mapped.buffer.open(scope); | ||
| if mapped.copy_on_unmap && mapped.size != 0 && ab.byte_length() != 0 { |
There was a problem hiding this comment.
Edge case worth a comment: if a caller does ab.transfer() on a writable range and writes into the transferred copy before unmap(), the ab.byte_length() != 0 guard sees the original as detached and silently skips writeback, dropping that write. It's safe (no UB) and arguably outside what the WebGPU mapping model contemplates, but a brief note here would document the intent.
Summary
ArrayBufferstorageBackground
Writable mapped ranges previously used backend memory directly, while readable ranges were copied without being retained for detachment. This made returned
ArrayBufferlifetimes depend on the native mapping and left readable ranges attached afterunmap().Keeping JavaScript ranges in owned storage makes their lifetime independent of the backend mapping. Writable data is copied back before the mapping closes, including when callers request multiple non-overlapping ranges.
Tests
cargo test -p deno_webgpucargo build -p deno -p test_servercargo test -p unit_tests --test unit webgpu_testcargo clippy -p deno_webgpu --all-targets -- -D warnings./tools/format.js ext/webgpu/buffer.rs tests/unit/webgpu_test.ts./tools/lint.js tests/unit/webgpu_test.ts