From 408729ca215ef5e9ff5127d878b042c4ff3849e9 Mon Sep 17 00:00:00 2001 From: Justin Anderson Date: Sat, 20 May 2023 00:45:57 -0700 Subject: [PATCH] Allow using active host context in hostfxr_get_runtime_delegate (#86460) These changes allow for passing `nullptr` for the handle parameter of the `hostfxr_get_runtime_delegate` function. This allows in-process callers who do not have the real host context handle to invoke this function and get a runtime delegate for the primary runtime instance. Calling hostfxr_get_runtime_delegate without the handle will only work if there is an active host context, otherwise it will return StatusCode::HostInvalidState. --- .../NativeHosting/HostContext.cs | 27 +++++++++++ src/native/corehost/fxr/fx_muxer.cpp | 46 ++++++++----------- src/native/corehost/fxr/fx_muxer.h | 3 +- src/native/corehost/fxr/hostfxr.cpp | 30 ++++++++++-- .../corehost/test/nativehost/host_context_test.cpp | 53 ++++++++++++++++++++++ .../corehost/test/nativehost/host_context_test.h | 4 ++ src/native/corehost/test/nativehost/nativehost.cpp | 4 ++ 7 files changed, 134 insertions(+), 33 deletions(-) diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/HostContext.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/HostContext.cs index 4e80ca7..a3db96a 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/HostContext.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/HostContext.cs @@ -21,6 +21,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting public const string Mixed = "mixed"; public const string NonContextMixedAppHost = "non_context_mixed_apphost"; public const string NonContextMixedDotnet = "non_context_mixed_dotnet"; + public const string GetRuntimeDelegateForActiveContext = "get_runtime_delegate_for_active_context"; } public class CheckProperties @@ -237,6 +238,32 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting propertyValidation.ValidateSecondaryContext(result, SharedTestState.SecondaryConfigPropertyName, SharedTestState.SecondaryConfigPropertyValue); } + [Fact] + public void GetDelegate_ActiveContext() + { + string newPropertyName = "HOST_TEST_PROPERTY"; + string[] args = + { + HostContextArg, + Scenario.GetRuntimeDelegateForActiveContext, + CheckProperties.None, + sharedState.HostFxrPath, + sharedState.RuntimeConfigPath, + SharedTestState.ConfigPropertyName, + newPropertyName + }; + CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNetRoot) + .Execute(); + + result.Should().Pass() + .And.InitializeContextForConfig(sharedState.RuntimeConfigPath) + .And.CreateDelegateMock_COM() + .And.CreateDelegateMock_InMemoryAssembly(); + + CheckPropertiesValidation propertyValidation = new CheckPropertiesValidation(CheckProperties.None, LogPrefix.Config, SharedTestState.ConfigPropertyName, SharedTestState.ConfigPropertyValue); + propertyValidation.ValidateActiveContext(result, newPropertyName); + } + [Theory] [InlineData(Scenario.Mixed, CheckProperties.None)] [InlineData(Scenario.Mixed, CheckProperties.Get)] diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index 20df946..efabe74 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -833,30 +833,27 @@ int fx_muxer_t::initialize_for_runtime_config( return rc; } -namespace +int fx_muxer_t::load_runtime(host_context_t *context) { - int load_runtime(host_context_t *context) - { - assert(context->type == host_context_type::initialized || context->type == host_context_type::active); - if (context->type == host_context_type::active) - return StatusCode::Success; - - const corehost_context_contract &contract = context->hostpolicy_context_contract; - int rc = contract.load_runtime(); + assert(context->type == host_context_type::initialized || context->type == host_context_type::active); + if (context->type == host_context_type::active) + return StatusCode::Success; - // Mark the context as active or invalid - context->type = rc == StatusCode::Success ? host_context_type::active : host_context_type::invalid; + const corehost_context_contract &contract = context->hostpolicy_context_contract; + int rc = contract.load_runtime(); - { - std::lock_guard lock{ g_context_lock }; - assert(g_active_host_context == nullptr); - g_active_host_context.reset(context); - g_context_initializing.store(false); - } + // Mark the context as active or invalid + context->type = rc == StatusCode::Success ? host_context_type::active : host_context_type::invalid; - g_context_initializing_cv.notify_all(); - return rc; + { + std::lock_guard lock{ g_context_lock }; + assert(g_active_host_context == nullptr); + g_active_host_context.reset(context); + g_context_initializing.store(false); } + + g_context_initializing_cv.notify_all(); + return rc; } int fx_muxer_t::run_app(host_context_t *context) @@ -874,7 +871,7 @@ int fx_muxer_t::run_app(host_context_t *context) { propagate_error_writer_t propagate_error_writer_to_corehost(context->hostpolicy_contract.set_error_writer); - int rc = load_runtime(context); + int rc = fx_muxer_t::load_runtime(context); if (rc != StatusCode::Success) return rc; @@ -882,7 +879,7 @@ int fx_muxer_t::run_app(host_context_t *context) } } -int fx_muxer_t::get_runtime_delegate(host_context_t *context, coreclr_delegate_type type, void **delegate) +int fx_muxer_t::get_runtime_delegate(const host_context_t *context, coreclr_delegate_type type, void **delegate) { switch (type) { @@ -913,13 +910,6 @@ int fx_muxer_t::get_runtime_delegate(host_context_t *context, coreclr_delegate_t { propagate_error_writer_t propagate_error_writer_to_corehost(context->hostpolicy_contract.set_error_writer); - if (context->type != host_context_type::secondary) - { - int rc = load_runtime(context); - if (rc != StatusCode::Success) - return rc; - } - return contract.get_runtime_delegate(type, delegate); } } diff --git a/src/native/corehost/fxr/fx_muxer.h b/src/native/corehost/fxr/fx_muxer.h index b3a0e00..6a9daeb 100644 --- a/src/native/corehost/fxr/fx_muxer.h +++ b/src/native/corehost/fxr/fx_muxer.h @@ -32,11 +32,12 @@ public: hostfxr_handle *host_context_handle); static int run_app(host_context_t *context); static int get_runtime_delegate( - host_context_t *context, + const host_context_t *context, coreclr_delegate_type delegate_type, void** delegate); static const host_context_t* get_active_host_context(); static int close_host_context(host_context_t *context); + static int load_runtime(host_context_t *context); private: static int handle_exec_host_command( const pal::string_t& host_command, diff --git a/src/native/corehost/fxr/hostfxr.cpp b/src/native/corehost/fxr/hostfxr.cpp index 0df1b24..a6710dc 100644 --- a/src/native/corehost/fxr/hostfxr.cpp +++ b/src/native/corehost/fxr/hostfxr.cpp @@ -688,14 +688,36 @@ SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_get_runtime_delegate( *delegate = nullptr; - host_context_t *context = host_context_t::from_handle(host_context_handle); - if (context == nullptr) - return StatusCode::InvalidArgFailure; - coreclr_delegate_type delegate_type = hostfxr_delegate_to_coreclr_delegate(type); if (delegate_type == coreclr_delegate_type::invalid) return StatusCode::InvalidArgFailure; + const host_context_t *context; + if (host_context_handle == nullptr) + { + context = fx_muxer_t::get_active_host_context(); + if (context == nullptr) + { + trace::error(_X("Hosting components context has not been initialized. Cannot get runtime properties.")); + return StatusCode::HostInvalidState; + } + } + else + { + host_context_t *context_from_handle = host_context_t::from_handle(host_context_handle); + if (context_from_handle == nullptr) + return StatusCode::InvalidArgFailure; + + if (context_from_handle->type != host_context_type::secondary) + { + int rc = fx_muxer_t::load_runtime(context_from_handle); + if (rc != StatusCode::Success) + return rc; + } + + context = context_from_handle; + } + return fx_muxer_t::get_runtime_delegate(context, delegate_type, delegate); } diff --git a/src/native/corehost/test/nativehost/host_context_test.cpp b/src/native/corehost/test/nativehost/host_context_test.cpp index 24b6495..b7c8c98 100644 --- a/src/native/corehost/test/nativehost/host_context_test.cpp +++ b/src/native/corehost/test/nativehost/host_context_test.cpp @@ -688,6 +688,49 @@ namespace return success && rcClose == StatusCode::Success; } + + bool get_runtime_delegate_for_active_context_test( + const hostfxr_exports &hostfxr, + const pal::char_t *config_path, + hostfxr_delegate_type delegate_type1, + hostfxr_delegate_type delegate_type2, + const pal::char_t *log_prefix, + pal::stringstream_t &test_output) + { + hostfxr_handle handle; + if (!init_for_config(hostfxr, config_path, &handle, log_prefix, test_output)) + return false; + + void *delegate1 = nullptr; + // Check that using get_runtime_delegate with the active host context will fail + // due to the runtime not being loaded yet. + bool success = get_runtime_delegate(hostfxr, nullptr, delegate_type1, &delegate1, log_prefix, test_output); + if (success) + { + test_output << log_prefix << _X("get_runtime_delegate with active context succeeded unexpectedly.") << std::endl; + return false; + } + if (nullptr != delegate1) + { + test_output << log_prefix << _X("Unexpectedly got a runtime delegate when get_runtime_delegate failed.") << std::endl; + return false; + } + + // Successfully get first delegate with a defined handle. + success = get_runtime_delegate(hostfxr, handle, delegate_type1, &delegate1, log_prefix, test_output); + + // Testing that using the active host context works for get_runtime_delegate + // by passing nullptr for handle parameter. The runtime must be loaded for this to succeed; + // the first successful call to get_runtime_delegate with a defined handle ensures that it is loaded. + void *delegate2; + success &= get_runtime_delegate(hostfxr, nullptr, delegate_type2, &delegate2, log_prefix, test_output); + + int rcClose = hostfxr.close(handle); + if (rcClose != StatusCode::Success) + test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl; + + return success && rcClose == StatusCode::Success; + } } host_context_test::check_properties host_context_test::check_properties_from_string(const pal::char_t *str) @@ -1000,3 +1043,13 @@ bool host_context_test::app_get_function_pointer( return app_get_function_pointer_test(hostfxr, argc, argv, config_log_prefix, test_output); } + +bool host_context_test::get_runtime_delegate_for_active_context( + const pal::string_t &hostfxr_path, + const pal::char_t *config_path, + pal::stringstream_t &test_output) +{ + hostfxr_exports hostfxr{ hostfxr_path }; + + return get_runtime_delegate_for_active_context_test(hostfxr, config_path, first_delegate_type, secondary_delegate_type, config_log_prefix, test_output); +} diff --git a/src/native/corehost/test/nativehost/host_context_test.h b/src/native/corehost/test/nativehost/host_context_test.h index 4d6b5d0..44d9fb9 100644 --- a/src/native/corehost/test/nativehost/host_context_test.h +++ b/src/native/corehost/test/nativehost/host_context_test.h @@ -103,4 +103,8 @@ namespace host_context_test int argc, const pal::char_t *argv[], pal::stringstream_t &test_output); + bool get_runtime_delegate_for_active_context( + const pal::string_t &hostfxr_path, + const pal::char_t *config_path, + pal::stringstream_t &test_output); } diff --git a/src/native/corehost/test/nativehost/nativehost.cpp b/src/native/corehost/test/nativehost/nativehost.cpp index b53db3a..214e2b9 100644 --- a/src/native/corehost/test/nativehost/nativehost.cpp +++ b/src/native/corehost/test/nativehost/nativehost.cpp @@ -207,6 +207,10 @@ int main(const int argc, const pal::char_t *argv[]) bool launch_as_if_dotnet = pal::strcmp(scenario, _X("non_context_mixed_dotnet")) == 0; success = host_context_test::non_context_mixed(check_properties, hostfxr_path, app_or_config_path, config_path, remaining_argc, remaining_argv, launch_as_if_dotnet, test_output); } + else if (pal::strcmp(scenario, _X("get_runtime_delegate_for_active_context")) == 0) + { + success = host_context_test::get_runtime_delegate_for_active_context(hostfxr_path, app_or_config_path, test_output); + } else { std::cerr << "Invalid scenario" << std::endl; -- 2.7.4