Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Add design doc for load assemblies through native host #5486

Closed

Conversation

vitek-karas
Copy link
Member

Design the native hosting API to load assembly and get a function pointer to a static managed method.

This is a spin-off from the native-hosting.md and its PR #5336. The assembly loading part of that PR seems to be agreed upon, so it's split into this PR.

@vitek-karas vitek-karas added enhancement Product code improvement that does NOT require public API changes/additions area-Host labels Mar 15, 2019
@vitek-karas vitek-karas added this to the 3.0 milestone Mar 15, 2019
@vitek-karas vitek-karas self-assigned this Mar 15, 2019
```
This API will
* Locate the assembly using the `assembly_path` and its `.runtimeconfig.json` and determine the frameworks it requires to run. (Note that only framework dependent components will be supported for now).
* If the process doesn't have CoreCLR loaded (more specifically `hostpolicy` library)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if there is no runtimeconfig.json and the runtime is not loaded? I think that may be a fairly common scenario (eg. a loosely built piece of managed code embedded into a piece of native code).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just like in comhost it's not a supported scenario right now. Currently the lack of .runtimeconfig.json indicates self-contained application. It would make sense to apply the same logic for components. Unfortunately supporting self-contained components is very tricky (if not impossible).

That said, the alternative would be that the host somehow magically picks a runtime. But it's not just about the runtime, it's about all frameworks.
.NET Framework didn't have this problem since the host picked the runtime to use and with it came all the frameworks (from GAC). There were no side-by-side frameworks or runtimes or anything like that.

In the .NET Core world, we need something to make the choice. So far both the runtime and the host try to stay clear of policy decisions (with various levels of success).

I assume that just like for COM activation, the components enabled for loading through this API will require to be built for it. Basically exactly because of this problem.

What we may allow is for components to load without .runtimeconfig.json if there already is runtime in the process. It would mean we state that we won't ever support self-contained components (since the lack of .runtimeconfig.json is the indication of self-contained), but that might be an OK solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we used a slightly different pattern...
Initialize
Start
LoadAssembly
CreateDelegate
Stop

Any number of these could be collapsed (eg. Initialize implies Start, Load & Create are collapsed, etc.). I feel that the most predominant native hosting scenario would like more control of initialization (eg. not relying on an on disk artifact).

You may then say, that this is being considered as a separate PR (per our offline discussion) and that this is just one building block of the full flow. In which case, perhaps we add an InitializeViaRuntimeConfig (which takes a path)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from all of the added complexity of allowing full control (discussed elsewhere)… this is basically a discussion between which part of the system should be able to participate in framework/runtime decisions.

The current proposal favors the components - basically we try to guarantee that they will work (by making sure that their framework requirements are met, and thus requiring them to specify such requirements).

If we go with the other approach where the host is favored, it would mean a much larger burden on the host to "do the right thing" for components to work.

Copy link
Member

@jeffschwMSFT jeffschwMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the overall design and I know we have scoped the initialization of coreclr out of this current design, but I was thinking more about the use of runtimeconfig.json as a means to initialize the runtime and I think that may be insufficient. I think we should add an Initialization path that finds, initializes, and starts the runtime. Then the nethost_load_assembly_method will do the load and return a method pointer.

@vitek-karas
Copy link
Member Author

@jeffschwMSFT The idea of allowing full runtime initialization just through API calls without any files on disk has been brought up in couple other places already. It's definitely doable, the problem is the complexity. .runtimeconfig.json can be pretty complex - we would have to model all of it in C-style API. And then also any potential additions to the .runtimeconfig.json format would have to be reflected in that API as well.

Definitely doable, the question is if it's worth it.

For the component loading scenario this would only make sense if we allowed components without .runtimeconfig.json (if they have it, then there's no need for this new API). As dicussed above, this is not as clear cut either unfortunately.

I personally would rather start smaller in this case.

@jeffschwMSFT
Copy link
Member

@vitek-karas I understand the trade-offs and the broader scenario. My ask is to ensure we are considering the time when we provide the host with more control. In my mind the native hosting scenario is that scenario where control is with the host, and not the component. Looking ahead, I want to ensure we do not regret allowing the component have control with this API and then add APIs that give control to the host. (Eg. will this API be useful in the case when we add Initialization). Hence my suggestion of leaving it as two separate steps (which really has no semantic difference) but avoid the implicit discovery of the runtimeconfig.json.

My opinion is leaning towards providing an InitializeViaRuntimeConfig to make initialization explicit and allow this API to fit well when there is (possibly) more ways to initialize the runtime via the hosting API.

@AaronRobinsonMSFT
Copy link
Member

AaronRobinsonMSFT commented Mar 15, 2019

My opinion is leaning towards providing an InitializeViaRuntimeConfig to make initialization explicit and allow this API to fit well when there is (possibly) more ways to initialize the runtime via the hosting API.

Can we have a compromise here and add a reserved void* that could be used in the future to accept a string that is in JSON (i.e. the same format and schema as on disk). This would permit the escape hatch requested by @jeffschwMSFT, permit a fully contained scenario, and make the API much simpler. I see no reason to create a complex API for something that is already expressed in JSON - there just isn't enough value to handle that scenario because we will always have the artifact on disk scenario. This also makes the implementation path relatively simple because instead of reading from disk we read from a string.

Documentation/design-docs/native-hosting-assembly-load.md Outdated Show resolved Hide resolved
Documentation/design-docs/native-hosting-assembly-load.md Outdated Show resolved Hide resolved
* Locate the assembly using the `assembly_path` and its `.runtimeconfig.json` and determine the frameworks it requires to run. (Note that only framework dependent components will be supported for now).
* If the process doesn't have CoreCLR loaded (more specifically `hostpolicy` library)
* Using the `.runtimeconfig.json` resolve required frameworks (just like if running an application) and load the runtime.
* Else the CoreCLR is already loaded, in that case validate that required frameworks for the component can be satisfied by the runtime.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove 'in that case' and move 'validate that required...' into a sub-bullet to better match the structure of the corresponding if case/bullet above

Currently there's no way to unload the runtime itself (and we don't have any plans to add this ability). That said, managed components will be loaded into isolated ALCs and thus the ALC unload feature can be used to unload the managed component (leaving the runtime still active in the process).
* How to expose this functionality - how to identify which component to unload (assembly name, one of the returned function pointers)?
* What happens with the returned function pointers - unloading the underlying code would lead to these function pointers to become invalid, likely causing undefined behavior when used.
* How to handle sharing - for performance and functionality reasons it would make a lot of sense to not load the same assembly twice - basically if the component loading API is used twice on the same assembly, the assembly is loaded only once and two separate function pointers are returned. Unloading may introduce unexpected failure modes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason / scenario where a consumer might explicitly not want sharing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sharing is tied with unloading. If we did introduce unloading then sharing becomes problematic (ref counting... or some other approach??)

@vitek-karas
Copy link
Member Author

This proposal has been more or less implemented in various PRs and is now part of the product. I integrated a description of the implemented solution into the native hosting doc, see PR #6680. As such this PR is no longer necessary.

@vitek-karas vitek-karas closed this Jun 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Host enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants