From 465294e25e6b324fa8942587e255510445a5da5e Mon Sep 17 00:00:00 2001 From: Samuel Kemp Date: Mon, 22 Jun 2026 15:16:10 +0100 Subject: [PATCH] WebGPU: make WebGpuContextFactory::Cleanup exception-safe and idempotent Detach the static contexts map and default instance before releasing them so the factory is left in a clean, reusable state even if Dawn's Metal teardown throws (an Objective-C NSException on macOS). Guard each release independently so a throw in one cannot leak the other or leave the statics dangling. Fixes the 'ReleaseEpFactory failed ... Unknown exception' teardown error and the per-register/unregister WGPUInstance leak. See #29206. --- .../core/providers/webgpu/webgpu_context.cc | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.cc b/onnxruntime/core/providers/webgpu/webgpu_context.cc index 3b2411d7b8ca3..7fcdecfc33724 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_context.cc @@ -1075,14 +1075,31 @@ void WebGpuContextFactory::ReleaseContext(int context_id) { void WebGpuContextFactory::Cleanup() { std::lock_guard lock(mutex_); - if (contexts_ != nullptr) { - delete contexts_; - contexts_ = nullptr; + // Detach the static state first so the factory is left in a clean, reusable + // state even if a destructor throws during teardown. On macOS, releasing + // Dawn's Metal objects can raise an Objective-C NSException (caught here by + // `catch (...)`); without detaching first, a throw in `delete contexts_` + // would leak `default_instance_` and leave the statics dangling, breaking any + // subsequent re-initialization and leaking a WGPUInstance per + // register/unregister cycle. Guard each release independently so a throw in + // one cannot leak the other. + auto* contexts = contexts_; + contexts_ = nullptr; + WGPUInstance instance = default_instance_; + default_instance_ = nullptr; + + try { + delete contexts; + } catch (...) { + LOGS_DEFAULT(WARNING) << "Exception while destroying WebGPU contexts during teardown; ignoring."; } - if (default_instance_ != nullptr) { - wgpuInstanceRelease(default_instance_); - default_instance_ = nullptr; + if (instance != nullptr) { + try { + wgpuInstanceRelease(instance); + } catch (...) { + LOGS_DEFAULT(WARNING) << "Exception while releasing WebGPU instance during teardown; ignoring."; + } } }