Fix C# native library loading in .NET 5 single-file apps#24744
Fix C# native library loading in .NET 5 single-file apps#24744jtattermusch merged 4 commits intogrpc:masterfrom
Conversation
|
Once this is approved, I'll also create a backport PR. |
|
Adhoc distribtests: https://g3c.corp.google.com/results/invocations/a25cde5a-bf1c-49cc-923e-315e96987e17 (including the newly added single file publish distribtest). |
| // don't seem to be shadowed by DNX-based projects at all. | ||
| return assembly.Location; | ||
| var assemblyLocation = assembly.Location; | ||
| if (assemblyLocation != null) |
There was a problem hiding this comment.
Actually, assembly.Location might be returning an empty string according to the docs:
https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file#api-incompatibility
I'll check which one is correct and update the code if needed.
|
https://github.com/grpc/grpc/blob/master/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs#L245 is another problem that needs to be fixed to make the single file scenario working. There is no libcoreclr.so on disk in the single file scenario. |
Ok, thanks for the tip. Do you have ideas on how to fix this problem? |
|
@jtattermusch please add a distribtest for the |
The best way to fix this problem is to use |
Thanks for the tip! Unfortunately Native.Load is only available on .NET Core 3+ (and at this point the newest we target is netstandard2.0) |
dce96e9 to
4e8de8d
Compare
|
The interop test failure is #24764. |
|
As @jkotas has pointed out, I'm now getting the |
|
@yulin-liang I don't have a fully working fix at this point, so I think it's fine to cut the release branch without it. (If I come up with a fix, I can backport it) |
The fix for this should be to try to lookup these methods using reflection, and then use them instead of the hardcoded P/Invokes when these methods are available. Something like: delegate bool TryLoadDelegate(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath, out IntPtr handle);
delegate bool TryGetExportDelegate(IntPtr handle, string name, out IntPtr address);
static TryLoadDelegate s_tryLoad;
static TryGetExportDelegate s_tryGetExport;
...
Type nativeLibrary = Type.GetType("System.Runtime.InteropServices.NativeLibrary, System.Runtime.InteropServices, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
if (nativeLibrary == null)
return;
MethodInfo tryLoad = nativeLibrary.GetMethod("TryLoad", BindingFlags.Static | BindingFlags.Public, null,
new Type[] { typeof(string), typeof(Assembly), typeof(Nullable<DllImportSearchPath>), typeof(IntPtr).MakeByRefType() }, null);
s_tryLoad = (TryLoadDelegate)tryLoad?.CreateDelegate(typeof(TryLoadDelegate));
MethodInfo tryGetExport = nativeLibrary.GetMethod("TryLoad", BindingFlags.Static | BindingFlags.Public, null,
new Type[] { typeof(IntPtr), typeof(string), typeof(IntPtr).MakeByRefType() }, null);
s_tryGetExport = (TryGetExportDelegate)tryGetExport?.CreateDelegate(typeof(TryGetExportDelegate)); |
|
Alternative approach used by a library that faced a similar problem: gui-cs/Terminal.Gui@19955fa#diff-91d94d0e5b8dd01a182c51299d7e8093247fa636a9768574b7042bfd5df9a1deR261 |
77212fd to
840590e
Compare
|
I refactored the native library loading logic to use the default The adhoc distribtest run is here: https://g3c.corp.google.com/results/invocations/4da3122d-8972-48ff-ac92-d01b22705b99/log |
|
Known failures: build_docker_go is also failing on master. |
|
Adhoc distribtests are passing: dotnet 5 distribtest is also green: |
| // TODO: allow customizing path to native extension (possibly through exposing a GrpcEnvironment property). | ||
| // See https://github.com/grpc/grpc/pull/7303 for one option. | ||
| var assemblyDirectory = Path.GetDirectoryName(GetAssemblyPath()); | ||
| var assemblyDirectory = GetAssemblyDirectory(); |
There was a problem hiding this comment.
The changes in this method should not be needed with your new fix.
| // The classes with the list of DllImport'd methods are code generated, | ||
| // so having more than just one doesn't really bother us. | ||
|
|
||
| // on Windows, the DllImport("grpc_csharp_ext.x64") doesn't work for some reason, |
There was a problem hiding this comment.
This was bug dotnet/coreclr#17505 . It is fixed in .NET Core 3.1, but I assume you still want this to work on .NET Core 2.1.
Looks fine to me. |
|
@jtattermusch does your fix handle the |
I haven't tested that explicitly - would you mind trying it out and posting back the result? The nightly build of the nugets should appear here soon: Alternatively, here are nugets from my last manual build: I wanted to merge this PR ASAP because like this the fixes will still make it to Grpc.Core 2.34.0 release - we can address |
|
@jtattermusch I did a test with |
Fixes #24266.