-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Fix Loading of Plugin Assemblies #6189
Conversation
Is there a way to create unit tests for this behavior? Edit: Spoke too soon, looks like tests that cover this already exist and are failing. |
Yup, I am working on fixing that now. |
src/Shared/CoreCLRAssemblyLoader.cs
Outdated
// the load is part of the platform, and load it using the Default ALC. | ||
string assemblyDir = Path.GetDirectoryName(fullPath); | ||
|
||
if (Traits.Instance.EscapeHatches.UseSingleLoadContext || string.Equals(assemblyDir, _msbuildDirPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is any normalization needed for this path, or can you guarantee that a string comparison is sufficient? Things that could matter: relative vs full path, casing on some platforms, directory separator normalization, whitespace, extended prefixes (eg \??\
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both should be full paths to the directory with MSBuild, assuming they match, and since they describe the exact same file system path, I think they should be identical in casing, too.
My related question is: is this the right MSBuild.exe? If we're running in, say, VS, this point to the version of MSBuild associated with VS, not the version that's executing at the moment. Is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a commit that I'll post shortly that will normalize the path in the same way that the input is normalized, just to be sure. The cost should be insignificant, as we only do it once per instance of MSBuild.
@Forgind on your related question, I believe that this is the right MSBuild based on what you're saying. We'd want to be pointing at the VS MSBuild in that case, right? That would ensure that we only load DLLs in the task ALC for tasks that aren't "part of MSBuild".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but I trust you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it easy to test this? Things like:
Task.dll
<msbuildDir>\sub\..\Task.dll
C:\<msBuildPath>\Task.dll
\<msbuildPath>\Task.dll
Presumably the path to the task is coming from the project/props/targets so it is user specified. I think there's also a version of UsingTask
that uses StrongName. Does that need to be considered?
Assuming that can be fed in at the right level.
// the load is part of the platform, and load it using the Default ALC. | ||
string assemblyDir = Path.GetDirectoryName(fullPath); | ||
|
||
if (Traits.Instance.EscapeHatches.UseSingleLoadContext || string.Equals(assemblyDir, _msbuildDirPath)) | ||
{ | ||
return LoadUsingLegacyDefaultContext(fullPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why is the default ALC called legacy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. This is beyond my time in this code base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The not-legacy ALC was a recent addition. rainersigwald tried to make it on by default, but that broke people, so he switched it to off-by-default but encouraged. It might have been turned on by default in the SDK; not sure.
It might be interesting to measure the perf difference of this change compared to before the regression. It may provide greater wins than the first fix since it is sharing more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is a reasonable fix to the issue we faced. If you need to expedite the fix consider it approved: this is much better than current behavior. I think it'd be good to add some test coverage but that could be a separate PR.
Discovered a bug around case sensitivity of paths. Consulted with @ladipro and landed at the latest iteration. |
@ericstj, I did some offline testing around the types of paths that you proposed above, and they are handled properly by the file path normalization logic. |
{ | ||
_msbuildDirPath = FileUtilities.NormalizePath(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath); | ||
_msbuildDirPath = FileUtilities.NormalizePath(typeof(CoreClrAssemblyLoader).Assembly.Location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ComparePathsNoThrow handles the normalization part of this for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @Forgind. I'd like to leave this here. The reason is that it happens only once per process, and has the ability to simplify the checks that must happen per assembly, instead of just once per process. If we don't do this here, and the value would have changed, then when loading an assembly, we'd have to do a string comparison which would fail, and then we'd have to normalize both inputs, and then try again.
If on the other hand, the initial normalize call here changed the input, it could possibly avoid the extra normalizations per assembly load.
src/Shared/CoreCLRAssemblyLoader.cs
Outdated
@@ -23,8 +23,15 @@ internal sealed class CoreClrAssemblyLoader | |||
|
|||
private bool _resolvingHandlerHookedUp = false; | |||
|
|||
private static string _msbuildDirPath; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super-nit: readonly
@marcpopMSFT, can you help me understand when this should be merged so that it doesn't impact the #6195? |
We have the final build for preview 2 now so we should be clear to get this in. However, our PR queue backed up again because of a sev 3 feed livesite incident where our build feed was taking many hours to update with new builds so we haulted our checkins. They say that's fixed now so we need to confirm and get our 11 approved PRs flowing again. |
Fixes #6186
Context
#6126 changed the load behavior for NuGet dlls by forcing them to load in the default assembly load context. This effectively pinned their versions to the SDK, which is not a safe behavior for targets that may wish to carry their own versions of NuGet or other DLLs that they might load.
Changes Made
Rather than pinning specific DLLs, change the behavior to load all DLLs that are in the MSBuild directory in the default assembly load context. This allows targets to carry their own versions of dependencies, while avoiding loading assemblies that are packaged with MSBuild into multiple assembly load contexts, and thus losing the benefits of precompilation.
Testing
Verified that dotnet build hello-world has the correct load and jitting behavior.
Verified that tasks can load their own copies of NuGet binaries into the plugin assembly load context.
Notes