Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of ComponentDependencyResolver #8

Closed
wants to merge 6 commits into from

Conversation

vitek-karas
Copy link
Owner

PInvokes into hostpolicy.dll (which should live next to the runtime and thus always be reachable).
If the PInvoke fails (missing hostpolicy.dll) we will fail for now.

Adds tests for the API into CoreCLR repo. The main reason is that with corerun
we can easily mock the hostpolicy.dll since there's none to start with.
Writing the same tests in CoreFX or any other place which starts the runtime through
hostpolicy would require test-only functionality to exist in either the class itself
or in the hostpolicy.

string resource_search_paths);

#pragma warning disable BCL0015 // Disable Pinvoke analyzer errors.
[DllImport("hostpolicy", CharSet = HostpolicyCharSet)]

Choose a reason for hiding this comment

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

Are we sure we want to take this dependency in CoreLib on hostpolicy? Also, it feels strange to have a corefx API depend on the hostpolicy assembly. What will happen in other scenarios that aren't using the dotnet.exe host? For example, Mono/Xamarin.

/cc @ericstj @jkotas

Copy link
Owner Author

Choose a reason for hiding this comment

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

The actual behavior of the API is very runtime specific. On .NET Core it relies on .deps.json. On .NET Framework (if we would have it there) it would rely on something completely different (probably just directory layout). On Mono I assume it will also rely on different mechanism, whatever is it that Mono uses to describe dependencies.

The dependency on hostpolicy is for two reasons:

  • Right now to avoid code duplication - all the logic around .deps.json is in hostpolicy and duplicating it elsewhere is just asking for bugs.
  • Technological limitations - we don't have a JSON parser which could be part of CoreLib. The eventual long term plan is to use the new JSON parser from ASP.NET and move the code from hostpolicy to CoreLib for both startup .deps.json processing as well as for this functionality.

As for what happens if the runtime is not hosted through hostpolicy, currently this API would fail. I'm still thinking of what is doable for .NET Core 3 RTM, options are:

  • Fallback to directory based behavior (basically just like if there's no .deps.json)
  • Introduce a mechanism for custom coreclr hosts to provide the same service as hostpolicy. Currently they basically have to do that for startup, so it would make sense to have that.

Choose a reason for hiding this comment

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

Would it make more sense to add these APIs to the Microsoft.Extensions.DependencyModel library instead? This library already knows how to read and interpret .deps.json files.

Copy link
Owner Author

Choose a reason for hiding this comment

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

The problem is layering:

  • That library has dependency on Newtonsoft.Json which is problematic (dependency on a specific version, which the app using the new API may not like, and so on)
  • Going forward we think we will need this API inside CoreLib itself. The main scenario is COM activation where CoreLib would use this API to be able to load the necessary dependencies for the assembly which contains the COM object being activated.

Choose a reason for hiding this comment

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

That library has dependency on Newtonsoft.Json which is problematic (dependency on a specific version, which the app using the new API may not like, and so on)

I was told that @steveharter was going to convert it to use the new JSON APIs instead on netcoreapp3.0+ (or netstandard2.1, if they make it into that). So that dependency will be going away in newer platforms. See https://github.com/dotnet/core-setup/issues/3311.

Going forward we think we will need this API inside CoreLib itself.

Ok, that is a good enough reason for me.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Thanks for the link to the issue - wasn't aware of it. Steve will not be the one doing it, that would be on me or the other Steve (@sdmaclea). I personally would very much prefer to move the code from hostpolicy which is currently all in C++ into corlib. That would solve several issues:

  • Our own productivity - C# is way faster to write/maintain than C++
  • No dependency on host (whatever that means)
  • Easy sharing of that functionality with other pieces - like this one
  • Ability to provide consistent diagnostics experience. Today one of the big problems of the host is that it has no ties to EventPipe/ETW, so diagnosing issues which span host and runtime is really tricky. (Adding support for ETW into host is a whole other topic, since it has its own PAL and everything...)

Copy link

Choose a reason for hiding this comment

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

Another thing to consider, if you can't move the code down, is to have CoreCLR expose this as part of its hosting APIs and and have hostpolicy implement that hosting extension. That way you allow for any CoreCLR host to plug into this. dotnet/hostpolicy isn't the only coreclr host.

Copy link

@sdmaclea sdmaclea left a comment

Choose a reason for hiding this comment

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

I haven't looked at the tests in detail yet.

}
catch (DllNotFoundException dllNotFoundException)
{
throw new InvalidOperationException(SR.ComponentDependencyResolver_FailedToLoadHostpolicy, dllNotFoundException);

Choose a reason for hiding this comment

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

Seems like these two should throw a NotSupportedException

Copy link
Owner Author

Choose a reason for hiding this comment

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

I guess it might make sense... it's true that these would be the "fallback" cases if we decide to implement some fallback behavior.

Choose a reason for hiding this comment

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

I guess I was thinking that the host was not supporting this....

// Something went wrong - report a failure
// TODO - once we have the ability to capture detailed error messages from the hostpolicy
// use it here to make the exception more actionable.
throw new InvalidOperationException(SR.Format(SR.ComponentDependencyResolver_FailedToResolveDependencies, componentAssemblyPath, returnCode));

Choose a reason for hiding this comment

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

This one might be the incorrect exception also.... It is not obvious why this would happen.

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is thrown when anything goes wrong with processing the .deps.json. With the future error redirection work, we will get exact error details, so the exception message will be much better. We could also change exception types based on the various error codes returned by the native code.

Typical example where this will fire:

  • The path to the component assembly is wrong (the file doesn't exist)
  • Invalid JSON in the .deps.json
  • Semantical error in the .deps.json - although there aren't that many cases where this would happen. The hostpolicy is pretty resilient to invalid inputs. (The one I know of is if there are duplicate entries in the .deps.json in which case hostpolicy doesn't know which one to pick)

// (CoreCLR gets this and stores it as the culture name in the internal assembly name)
// AssemblyName.CultureName is just a shortcut to AssemblyName.Culture.Name.
if (!string.IsNullOrEmpty(assemblyName.CultureName) &&
!string.Equals(assemblyName.CultureName, "neutral", StringComparison.OrdinalIgnoreCase))

Choose a reason for hiding this comment

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

I haven't seen the CultureName == "neutral" when looking at resolving event. Maybe there are code paths I haven't looked at. I wonder if this is dead native code.

// Search resource search paths by appending the culture name and the expected assembly file name.
// Copies the logic in BindSatelliteResourceByResourceRoots in CoreCLR.
// Note that the runtime will also probe APP_PATHS the same way, but that feature is effectively
// being deprecated, so we chose to not support the same behavior for components.

Choose a reason for hiding this comment

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

This logic seems strange. I will look at this as I step through the code again...

return null;
}

public string ResolveUnmanagedDllPath(string unmanagedDllName)

Choose a reason for hiding this comment

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

Please add a note here that the the CDR only resolves from the the component directory, deps.json paths, and simple name variants. For example: It doesn't respect DllSearchPath attributes on the Module or method. Further resolution should be handled by DllImport callbacks and resolvers.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Would it be fair to say something like:
"This only resolves the native library path by using the component directory, .deps.json information and simple name variants. It won't resolve libraries which would rely on the OS library resolution behavior. It should be used only as one part of a fully featured native library loading mechanism."
We should discuss the details here, as I need to understand what exactly the different options are...

PInvokes into hostpolicy.dll (which should live next to the runtime and thus always be reachable).
If the PInvoke fails (missing hostpolicy.dll) we will fail for now.

Adds tests for the API into CoreCLR repo. The main reason is that with corerun
we can easily mock the hostpolicy.dll since there's none to start with.
Writing the same tests in CoreFX or any other place which starts the runtime through
hostpolicy would require test-only functionality to exist in either the class itself
or in the hostpolicy.
 Add native resolution tests
Registers error writer with the hostpolicy to receive detailed errors. Uses that in the exception.

Modifications to the mock and the tests to be able to verify the functionality.
@sdmaclea
Copy link

@vitek-karas Seems you can close this PR

@vitek-karas
Copy link
Owner Author

Abandoning - this was used as the base change for the implementation of AssemblyDependencyResolver which is already in coreclr/corefx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants