Skip to content

[WebGPU] Plugin EP 0.2.0 release cherry picks round 1#29630

Open
edgchen1 wants to merge 2 commits into
plugin-ep-webgpu/rel-0.2from
edgchen1/plugin-ep-webgpu/rel-0.2/cherry_pick_1
Open

[WebGPU] Plugin EP 0.2.0 release cherry picks round 1#29630
edgchen1 wants to merge 2 commits into
plugin-ep-webgpu/rel-0.2from
edgchen1/plugin-ep-webgpu/rel-0.2/cherry_pick_1

Conversation

@edgchen1

@edgchen1 edgchen1 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

This cherry-picks the following commits for the release:

Commit ID PR Number Commit Title
ef73996 #29591 [WebGPU] Fix OrtReleaseEnv self-deadlock after failed adapter request on Linux
c57e0e5 #29625 Avoid throwing C++ exceptions from WebGPU EP Dawn callbacks

Motivation and Context

0.2.0 cherry picks, round 1.

Copilot AI and others added 2 commits July 8, 2026 16:32
… on Linux (#29591)

On Linux with the WebGPU EP, if `SessionOptionsAppendExecutionProvider`
is called when no Vulkan adapter is available, a subsequent
`OrtReleaseEnv` on the same thread hangs forever in a futex wait.

### Root causes

**1. C++ exceptions thrown inside Dawn `WaitAny` callbacks (primary)**

`ORT_ENFORCE` was called directly inside the
`RequestAdapter`/`RequestDevice` callback lambdas. Dawn's `WaitAny` does
not release its internal `EventManager` mutex on exception, so throwing
through it leaves that mutex permanently locked. The deadlock fires
later when `Cleanup()` calls `wgpuInstanceRelease` →
`EventManager::ShutDown()` → tries to re-acquire the same mutex on the
same thread.

**2. Zombie `WebGpuContext` left in the factory map (secondary)**

When `Initialize()` threw, the `WebGpuContext` entry remained in
`contexts_` with `ref_count=1` and no owner — a resource leak that would
also re-trigger the deadlock path at `Cleanup()`.

### Changes (`webgpu_context.cc`)

- **Adapter callback**: replace the throwing lambda with a non-throwing
one that writes into a local `RequestAdapterResult` struct;
`ORT_ENFORCE` is moved to *after* `WaitAny` returns:

  ```cpp
  // Before — throws inside Dawn's callback dispatch:
  [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter,
     wgpu::StringView message, wgpu::Adapter* ptr) {
      ORT_ENFORCE(status == wgpu::RequestAdapterStatus::Success, ...);
      *ptr = std::move(adapter);
  }, &adapter

  // After — captures result, throws outside Dawn:
  [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter,
     wgpu::StringView message, RequestAdapterResult* result) {
      result->status = status;
      if (status == wgpu::RequestAdapterStatus::Success)
          result->adapter = std::move(adapter);
      else
          result->message = std::string{message};
  }, &adapter_result
// ORT_ENFORCE(adapter_result.status == ...) called here, after WaitAny
  ```

- **Device callback**: same pattern applied to `RequestDevice`.

- **`CreateContext` cleanup**: wrap `Initialize()` in a `try/catch`; on
failure decrement `ref_count` and erase the entry from `contexts_` if it
reaches zero.

### Motivation and Context

Fixes the hang reported when registering the WebGPU EP with no usable
Vulkan adapter and then calling `OrtReleaseEnv`. The process would park
on a futex with `__owner` set to its own TID — a non-recursive mutex
locked twice on the same thread — and never exit.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
### Description
<!-- Describe your changes. -->

Mark every Dawn callback (RequestAdapter, RequestDevice,
SetUncapturedErrorCallback, SetDeviceLostCallback, MapAsync,
PopErrorScope, CreateComputePipelineAsync) noexcept and avoid throwing
from inside them. A C++ exception unwinding out of a Dawn callback runs
while Dawn holds internal locks and is undefined behavior, and can leave
the EventManager mutex locked so a later instance release
self-deadlocks.

The MapAsync callbacks in BufferManager::Download and
WebGpuContext::CollectProfilingData now write the status and message
into a MapAsyncResult struct and the outcome is checked after Wait
returns (ORT_THROW_IF_ERROR + ORT_ENFORCE).

PopErrorScope builds and returns an onnxruntime::Status for both a
failed pop and a validation error instead of enforcing inside the
callback.

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

Fix crashes caused by exceptions crossing the WebGPU callback boundary.

Follow up to #29591 with similar changes for other callbacks.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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.

3 participants