[WebGPU] Fix deferred pipeline callback lifetime after failed waits (#33209)
### Description
<!-- Describe your changes. -->
Share `PipelineCallbackContext` ownership between `PendingPipelineBuild`
and Dawn's one-shot pipeline callback instead of passing a raw pointer
to state owned solely by the pending build.
A failed wait can still discard deferred dispatches and return failure,
but the callback retains its result state until completion or
cancellation. The callback captures only the result state—not
`WebGpuContext` or `CommandRecordingState`—and releases its ownership
when invoked. Successful pipeline creation and caching behavior remain
unchanged.
Add regression tests covering:
- Successful pipeline completion and callback-state cleanup.
- A failed wait followed by late callback delivery after deferred
dispatches are cleared.
- Callback delivery and cleanup after the ORT WebGPU context is
destroyed.
Update the ownership documentation and document the remaining
cancellation-test coverage gap. No public API/ABI, replay-operator,
GenAI, or checked-completion behavior changes are included.
### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->
`ProgramManager::Build` previously passed Dawn a raw
`PipelineCallbackContext*` whose lifetime depended on a `unique_ptr` in
the deferred dispatch state. If `Wait(build.future)` failed,
`EncodeDeferredDispatches` could clear that state while Dawn still had
an outstanding callback. A later callback could then write through the
freed pointer.
Shared ownership makes callback-state lifetime independent of dispatch
cleanup without adding a permanent retention list or retaining ORT
context objects.
**Validation**
Native Release provider tests and the shared WebGPU plugin both rebuilt
successfully:
```bash
cmake --build build/webgpu-pipeline-lifetime/Release --target
onnxruntime_provider_test --parallel 32
cmake --build build/webgpu-plugin-20260929/Release --target
onnxruntime_providers_webgpu --parallel 8
```
Context, concurrent-context, and data-transfer regressions on Mesa
lavapipe: **54 passed, 1 expected configuration skip**.
```bash
cd build/webgpu-pipeline-lifetime/Release
VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json \
./onnxruntime_provider_test \
--gtest_filter='WebGpuContextTest.*:WebGpuConcurrentContextTestStandalone.*:WebGpuDataTransferTest.*'
```
The three focused lifetime tests also passed across **20 repetitions (60
executions)**:
```bash
VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json \
./onnxruntime_provider_test \
--gtest_filter='WebGpuContextTest.SuccessfulPipelineBuildReleasesCallbackOwnership:WebGpuContextTest.FailedPipelineWait*'
\
--gtest_repeat=20
```
The failed-wait tests deterministically use an instance without
`TimedWaitAny` and a ready-but-undelivered validation-error pipeline
callback. Actual native instance-shutdown cancellation is not injected
because outstanding Dawn events can retain the instance; delivery after
ORT context destruction and final callback-state reclamation are
directly verified.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>