From b467090c7d45c713e7c5e97849268781a73d8ad8 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:07:49 -0800 Subject: [PATCH 1/2] Fix WebGPU static destruction crash by heap-allocating contexts map --- .../core/providers/webgpu/webgpu_context.cc | 28 ++++++++++++------- .../core/providers/webgpu/webgpu_context.h | 7 ++++- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.cc b/onnxruntime/core/providers/webgpu/webgpu_context.cc index 318e815255ff7..cd40a261e559a 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_context.cc @@ -922,7 +922,7 @@ void WebGpuContext::ReleaseGraphResources(std::vector WebGpuContextFactory::contexts_; +std::unordered_map* WebGpuContextFactory::contexts_ = nullptr; std::mutex WebGpuContextFactory::mutex_; std::once_flag WebGpuContextFactory::init_default_flag_; wgpu::Instance WebGpuContextFactory::default_instance_; @@ -958,6 +958,11 @@ WebGpuContext& WebGpuContextFactory::CreateContext(const WebGpuContextConfig& co std::lock_guard lock(mutex_); + // Lazy-allocate the contexts map on first use (heap-allocated to avoid static destruction crash). + if (contexts_ == nullptr) { + contexts_ = new std::unordered_map(); + } + if (default_instance_ == nullptr) { // Create wgpu::Instance wgpu::InstanceFeatureName required_instance_features[] = {wgpu::InstanceFeatureName::TimedWaitAny}; @@ -981,15 +986,15 @@ WebGpuContext& WebGpuContextFactory::CreateContext(const WebGpuContextConfig& co "WebGPU EP custom context (contextId>0) must have custom WebGPU instance and device."); } - auto it = contexts_.find(context_id); - if (it == contexts_.end()) { + auto it = contexts_->find(context_id); + if (it == contexts_->end()) { GSL_SUPPRESS(r.11) auto context = std::unique_ptr(new WebGpuContext(instance, device, config.validation_mode, config.preserve_device, config.max_storage_buffer_binding_size)); - it = contexts_.emplace(context_id, WebGpuContextFactory::WebGpuContextInfo{std::move(context), 0}).first; + it = contexts_->emplace(context_id, WebGpuContextFactory::WebGpuContextInfo{std::move(context), 0}).first; } else if (context_id != 0) { ORT_ENFORCE(it->second.context->instance_.Get() == instance && it->second.context->device_.Get() == device, @@ -1006,8 +1011,9 @@ WebGpuContext& WebGpuContextFactory::CreateContext(const WebGpuContextConfig& co WebGpuContext& WebGpuContextFactory::GetContext(int context_id) { std::lock_guard lock(mutex_); - auto it = contexts_.find(context_id); - ORT_ENFORCE(it != contexts_.end(), "WebGPU EP context ID ", context_id, " is not found."); + ORT_ENFORCE(contexts_ != nullptr, "WebGPU contexts have not been initialized or have been cleaned up."); + auto it = contexts_->find(context_id); + ORT_ENFORCE(it != contexts_->end(), "WebGPU EP context ID ", context_id, " is not found."); return *it->second.context; } @@ -1015,17 +1021,19 @@ WebGpuContext& WebGpuContextFactory::GetContext(int context_id) { void WebGpuContextFactory::ReleaseContext(int context_id) { std::lock_guard lock(mutex_); - auto it = contexts_.find(context_id); - ORT_ENFORCE(it != contexts_.end(), "WebGPU EP context ID ", context_id, " is not found."); + ORT_ENFORCE(contexts_ != nullptr, "WebGPU contexts have not been initialized or have been cleaned up."); + auto it = contexts_->find(context_id); + ORT_ENFORCE(it != contexts_->end(), "WebGPU EP context ID ", context_id, " is not found."); if (--it->second.ref_count == 0 && !it->second.context->preserve_device_) { - contexts_.erase(it); + contexts_->erase(it); } } void WebGpuContextFactory::Cleanup() { std::lock_guard lock(mutex_); - contexts_.clear(); + delete contexts_; + contexts_ = nullptr; default_instance_ = nullptr; } diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index 7645f1e6b2482..790fdfeb580ac 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -147,7 +147,12 @@ class WebGpuContextFactory { private: WebGpuContextFactory() {} - static std::unordered_map contexts_; + // Use pointers to heap-allocated objects so that their destructors do NOT run + // during static destruction at process exit. This avoids crashes when dependent + // DLLs (e.g. dxcompiler.dll) have already been unloaded by the OS. + // Cleanup() explicitly deletes them during normal unload via ReleaseEpFactory. + // On abnormal/process termination they simply leak, which is safe. + static std::unordered_map* contexts_; static std::mutex mutex_; static std::once_flag init_default_flag_; static wgpu::Instance default_instance_; From 4082252e0f7d39bfc3f9c30d1e04c8ef875e3bb3 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:27:44 -0800 Subject: [PATCH 2/2] Update onnxruntime/core/providers/webgpu/webgpu_context.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/providers/webgpu/webgpu_context.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index 790fdfeb580ac..6b9b538483f16 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -150,7 +150,9 @@ class WebGpuContextFactory { // Use pointers to heap-allocated objects so that their destructors do NOT run // during static destruction at process exit. This avoids crashes when dependent // DLLs (e.g. dxcompiler.dll) have already been unloaded by the OS. - // Cleanup() explicitly deletes them during normal unload via ReleaseEpFactory. + // Cleanup() explicitly deletes them during normal unload. In the shared-library + // build this is reached via ReleaseEpFactory, and in the WebGPU static-lib build + // it is reached from OrtEnv::~OrtEnv via CleanupWebGpuContexts(). // On abnormal/process termination they simply leak, which is safe. static std::unordered_map* contexts_; static std::mutex mutex_;