Allow using active host context in hostfxr_get_runtime_delegate (#86460)
authorJustin Anderson <jander-msft@users.noreply.github.com>
Sat, 20 May 2023 07:45:57 +0000 (00:45 -0700)
committerGitHub <noreply@github.com>
Sat, 20 May 2023 07:45:57 +0000 (09:45 +0200)
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.

src/installer/tests/HostActivation.Tests/NativeHosting/HostContext.cs
src/native/corehost/fxr/fx_muxer.cpp
src/native/corehost/fxr/fx_muxer.h
src/native/corehost/fxr/hostfxr.cpp
src/native/corehost/test/nativehost/host_context_test.cpp
src/native/corehost/test/nativehost/host_context_test.h
src/native/corehost/test/nativehost/nativehost.cpp

index 4e80ca7..a3db96a 100644 (file)
@@ -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)]
index 20df946..efabe74 100644 (file)
@@ -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<std::mutex> 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<std::mutex> 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);
     }
 }
index b3a0e00..6a9daeb 100644 (file)
@@ -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,
index 0df1b24..a6710dc 100644 (file)
@@ -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);
 }
 
index 24b6495..b7c8c98 100644 (file)
@@ -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);
+}
index 4d6b5d0..44d9fb9 100644 (file)
@@ -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);
 }
index b53db3a..214e2b9 100644 (file)
@@ -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;