From 27f60d351d601263ce24c4871d726712692bfe1f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 9 Jul 2026 13:11:57 -0700 Subject: [PATCH 1/6] Cache COM activation delegate in comhost DllGetClassObject resolved the COM activation delegate on every call, which loaded hostfxr and parsed the app's runtimeconfig.json (creating a new secondary host context) each time. For applications that create COM objects implemented in .NET in a tight loop this is a significant, repeated cost. Resolve the activation delegate once and cache it for the process lifetime (the runtime is never unloaded). Failures are not cached, so a transient failure is retried on the next activation. This reduces the per-activation runtimeconfig.json parsing from once per call to once per process. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 63 +++++++++++++++++++------ 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index e61dbd0572bb1c..f31c07e8356546 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -10,6 +10,8 @@ #include "error_codes.h" #include "utils.h" #include +#include +#include #include #include @@ -170,6 +172,46 @@ namespace return status; } + // COM activation state resolved from the runtime. It is set for the process lifetime. + struct com_activation_info + { + pal::string_t app_path; + com_delegates delegates; + void* load_context; + }; + + // Resolve and cache the COM activation delegate so the hostfxr load and config parsing + // only happens once per comhost. Failures are not cached, so they are retried. + int get_com_activation_delegate(const com_activation_info** delegate) + { + static std::atomic com_activation_initialized{ false }; + static pal::mutex_t com_activation_lock; + static com_activation_info com_activation; + + if (!com_activation_initialized.load()) + { + std::lock_guard lock{ com_activation_lock }; + if (!com_activation_initialized.load()) + { + trace::setup(); + reset_redirected_error_writer(); + + error_writer_scope_t writer_scope(redirected_error_writer); + + int ec = get_com_delegate(hostfxr_delegate_type::hdt_com_activation, &com_activation.app_path, com_activation.delegates, &com_activation.load_context); + if (ec != StatusCode::Success) + return ec; + + assert(com_activation.delegates.delegate != nullptr || com_activation.load_context == ISOLATED_CONTEXT); + + com_activation_initialized.store(true); + } + } + + *delegate = &com_activation; + return StatusCode::Success; + } + void report_com_error_info(const GUID& guid, pal::string_t errs) { ICreateErrorInfo *cei; @@ -213,16 +255,9 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( return CLASS_E_CLASSNOTAVAILABLE; HRESULT hr; - pal::string_t app_path; - com_delegates act; - void* load_context; + const com_activation_info* activation; { - trace::setup(); - reset_redirected_error_writer(); - - error_writer_scope_t writer_scope(redirected_error_writer); - - int ec = get_com_delegate(hostfxr_delegate_type::hdt_com_activation, &app_path, act, &load_context); + int ec = get_com_activation_delegate(&activation); if (ec != StatusCode::Success) { report_com_error_info(rclsid, std::move(get_redirected_error_string())); @@ -230,8 +265,6 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( } } - assert(act.delegate != nullptr || load_context == ISOLATED_CONTEXT); - // Query the CLR for the type IUnknown *classFactory = nullptr; @@ -239,18 +272,18 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( { rclsid, riid, - app_path.c_str(), + activation->app_path.c_str(), iter->second.assembly.c_str(), iter->second.type.c_str(), (void**)&classFactory }; - if (act.delegate != nullptr) + if (activation->delegates.delegate != nullptr) { - RETURN_IF_FAILED(act.delegate(&cxt, load_context)); + RETURN_IF_FAILED(activation->delegates.delegate(&cxt, activation->load_context)); } else { - RETURN_IF_FAILED(act.delegate_no_load_cxt(&cxt)); + RETURN_IF_FAILED(activation->delegates.delegate_no_load_cxt(&cxt)); } assert(classFactory != nullptr); From 8bfd25c6d6f462f9fa14566bea1bff8270b9c334 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 10 Jul 2026 09:46:44 -0700 Subject: [PATCH 2/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index f31c07e8356546..ca4c7e99ba03f5 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -172,7 +172,7 @@ namespace return status; } - // COM activation state resolved from the runtime. It is set for the process lifetime. + // COM activation state resolved from the runtime. Cached for the lifetime of the loaded comhost module. struct com_activation_info { pal::string_t app_path; From 13fa3219f266500cb75e4ea477dca44b6b17cf72 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 10 Jul 2026 10:34:07 -0700 Subject: [PATCH 3/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index ca4c7e99ba03f5..ebba90f519e484 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -188,10 +188,10 @@ namespace static pal::mutex_t com_activation_lock; static com_activation_info com_activation; - if (!com_activation_initialized.load()) + if (!com_activation_initialized.load(std::memory_order_acquire)) { std::lock_guard lock{ com_activation_lock }; - if (!com_activation_initialized.load()) + if (!com_activation_initialized.load(std::memory_order_relaxed)) { trace::setup(); reset_redirected_error_writer(); @@ -204,7 +204,7 @@ namespace assert(com_activation.delegates.delegate != nullptr || com_activation.load_context == ISOLATED_CONTEXT); - com_activation_initialized.store(true); + com_activation_initialized.store(true, std::memory_order_release); } } From b0804eb0a8aa5818fa27c470537707361f1e9638 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 10 Jul 2026 10:45:06 -0700 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index ebba90f519e484..2081861c2c2b74 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -182,7 +182,7 @@ namespace // Resolve and cache the COM activation delegate so the hostfxr load and config parsing // only happens once per comhost. Failures are not cached, so they are retried. - int get_com_activation_delegate(const com_activation_info** delegate) + int get_com_activation_delegate(const com_activation_info** activation_info) { static std::atomic com_activation_initialized{ false }; static pal::mutex_t com_activation_lock; @@ -208,7 +208,7 @@ namespace } } - *delegate = &com_activation; + *activation_info = &com_activation; return StatusCode::Success; } From e820fcb447c94a95f4a135face57b7813a8b1063 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 10 Jul 2026 13:46:16 -0700 Subject: [PATCH 5/6] Use call_once Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 72 ++++++++++--------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index 2081861c2c2b74..6a6da7feafa473 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -10,7 +10,6 @@ #include "error_codes.h" #include "utils.h" #include -#include #include #include #include @@ -175,42 +174,15 @@ namespace // COM activation state resolved from the runtime. Cached for the lifetime of the loaded comhost module. struct com_activation_info { + int status; pal::string_t app_path; com_delegates delegates; void* load_context; + pal::string_t error; }; - // Resolve and cache the COM activation delegate so the hostfxr load and config parsing - // only happens once per comhost. Failures are not cached, so they are retried. - int get_com_activation_delegate(const com_activation_info** activation_info) - { - static std::atomic com_activation_initialized{ false }; - static pal::mutex_t com_activation_lock; - static com_activation_info com_activation; - - if (!com_activation_initialized.load(std::memory_order_acquire)) - { - std::lock_guard lock{ com_activation_lock }; - if (!com_activation_initialized.load(std::memory_order_relaxed)) - { - trace::setup(); - reset_redirected_error_writer(); - - error_writer_scope_t writer_scope(redirected_error_writer); - - int ec = get_com_delegate(hostfxr_delegate_type::hdt_com_activation, &com_activation.app_path, com_activation.delegates, &com_activation.load_context); - if (ec != StatusCode::Success) - return ec; - - assert(com_activation.delegates.delegate != nullptr || com_activation.load_context == ISOLATED_CONTEXT); - - com_activation_initialized.store(true, std::memory_order_release); - } - } - - *activation_info = &com_activation; - return StatusCode::Success; - } + std::once_flag s_com_activation_flag; + com_activation_info s_com_activation; void report_com_error_info(const GUID& guid, pal::string_t errs) { @@ -255,14 +227,28 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( return CLASS_E_CLASSNOTAVAILABLE; HRESULT hr; - const com_activation_info* activation; + + // Resolve and store the COM activation delegate info. + std::call_once(s_com_activation_flag, []() { - int ec = get_com_activation_delegate(&activation); - if (ec != StatusCode::Success) - { - report_com_error_info(rclsid, std::move(get_redirected_error_string())); - return __HRESULT_FROM_WIN32(ec); - } + trace::setup(); + reset_redirected_error_writer(); + + error_writer_scope_t writer_scope(redirected_error_writer); + + s_com_activation.status = get_com_delegate(hostfxr_delegate_type::hdt_com_activation, &s_com_activation.app_path, s_com_activation.delegates, &s_com_activation.load_context); + if (s_com_activation.status != StatusCode::Success) + s_com_activation.error = get_redirected_error_string(); + + assert(s_com_activation.status != StatusCode::Success + || s_com_activation.delegates.delegate != nullptr + || s_com_activation.load_context == ISOLATED_CONTEXT); + }); + + if (s_com_activation.status != StatusCode::Success) + { + report_com_error_info(rclsid, s_com_activation.error); + return __HRESULT_FROM_WIN32(s_com_activation.status); } // Query the CLR for the type @@ -272,18 +258,18 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( { rclsid, riid, - activation->app_path.c_str(), + s_com_activation.app_path.c_str(), iter->second.assembly.c_str(), iter->second.type.c_str(), (void**)&classFactory }; - if (activation->delegates.delegate != nullptr) + if (s_com_activation.delegates.delegate != nullptr) { - RETURN_IF_FAILED(activation->delegates.delegate(&cxt, activation->load_context)); + RETURN_IF_FAILED(s_com_activation.delegates.delegate(&cxt, s_com_activation.load_context)); } else { - RETURN_IF_FAILED(activation->delegates.delegate_no_load_cxt(&cxt)); + RETURN_IF_FAILED(s_com_activation.delegates.delegate_no_load_cxt(&cxt)); } assert(classFactory != nullptr); From c0fecbfd15159a991191fb8fc24ec1c1b3c7c8c1 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 10 Jul 2026 13:55:30 -0700 Subject: [PATCH 6/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/native/corehost/comhost/comhost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/corehost/comhost/comhost.cpp b/src/native/corehost/comhost/comhost.cpp index 6a6da7feafa473..5906f3ed37d547 100644 --- a/src/native/corehost/comhost/comhost.cpp +++ b/src/native/corehost/comhost/comhost.cpp @@ -184,7 +184,7 @@ namespace std::once_flag s_com_activation_flag; com_activation_info s_com_activation; - void report_com_error_info(const GUID& guid, pal::string_t errs) + void report_com_error_info(const GUID& guid, const pal::string_t& errs) { ICreateErrorInfo *cei; if (!errs.empty() && SUCCEEDED(::CreateErrorInfo(&cei)))