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

Overhaul how the native extension is found, loaded and used #7401

Merged
merged 1 commit into from
Jul 15, 2016

Conversation

jskeet
Copy link
Contributor

@jskeet jskeet commented Jul 15, 2016

The goal of this is to fix #7230.

The changes here are:

  • The layout in the nuget package; the files are now in
    /runtimes/{os}/native/{library}
  • The filename of each library, which now includes the architecture,
    e.g grpc_csharp_ext.x64.dll
  • The targets file used to copy those files in msbuild-based projects;
    note that we now don't build up a folder structure.
  • The way the functions are found

Before this change, on Linux and OSX we used to find library symbols
manually, and use DllImport on Windows. With this change, the name
of the library file changes based on architecture, so DllImport
doesn't work. Instead, we have to use GetProcAddress to fetch the
function. This is further convoluted by the convention on
Windows-x86 to prefix the function name with _ and suffix it based
on the stack size of the arguments. We can't easily tell the
argument size here, so we just try 0, 4, 8...128. (128 bytes should
be enough for anyone.) This is inefficient, but it's a one-time hit
with a known number of functions, and doesn't seem to have any
significant impact.

The benefit of this in code is we don't need the DllImports any
more, and we don't need to conditionally use FindSymbol - we just
use it for everything, so things are rather more uniform and tidy.

The further benefit of this is that the library name is no longer
tied to a particular filename format - so if someone wanted to have
a directory with the libraries for every version in, with the
version in the filename, we'd handle that just fine. (At least once

Testing:

  • Windows:
    • Console app under msbuild, dotnet cli and DNX
    • ASP.NET Classic under msbuild
    • ASP.NET Core (still running under net451)
  • Ubuntu 16.04
    • Console app under dotnet cli, run with dotnet run and mono
  • OSX:
    • Console app under dotnet cli, run with dotnet run and mono

Under dotnet cli, a dependency on Microsoft.NETCore.Platforms is
required in order to force the libraries to be copied.

This change does not further enable .NET Core. It attempts to
keep the existing experimental .NET Core project files in line
with the msbuild files, but I expect further work to be required
for .NET Core, which has a different build/publication model.

The goal of this is to fix grpc#7230.

The changes here are:

- The layout in the nuget package; the files are now in
  `/runtimes/{os}/native/{library}`
- The filename of each library, which now includes the architecture,
  e.g `grpc_csharp_ext.x64.dll`
- The targets file used to copy those files in msbuild-based projects;
  note that we now don't build up a folder structure.
- The way the functions are found

Before this change, on Linux and OSX we used to find library symbols
manually, and use DllImport on Windows. With this change, the name
of the library file changes based on architecture, so `DllImport`
doesn't work. Instead, we have to use `GetProcAddress` to fetch the
function. This is further convoluted by the convention on
Windows-x86 to prefix the function name with `_` and suffix it based
on the stack size of the arguments. We can't easily tell the
argument size here, so we just try 0, 4, 8...128. (128 bytes should
be enough for anyone.) This is inefficient, but it's a one-time hit
with a known number of functions, and doesn't seem to have any
significant impact.

The benefit of this in code is we don't need the DllImports any
more, and we don't need to conditionally use `FindSymbol` - we just
use it for everything, so things are rather more uniform and tidy.

The further benefit of this is that the library name is no longer
tied to a particular filename format - so if someone wanted to have
a directory with the libraries for every version in, with the
version in the filename, we'd handle that just fine. (At least once

Testing:

- Windows:
  - Console app under msbuild, dotnet cli and DNX
  - ASP.NET Classic under msbuild
  - ASP.NET Core (still running under net451)
- Ubuntu 16.04
  - Console app under dotnet cli, run with dotnet run and mono
- OSX:
  - Console app under dotnet cli, run with dotnet run and mono

Under dotnet cli, a dependency on `Microsoft.NETCore.Platforms` is
required in order to force the libraries to be copied.

This change does *not* further enable .NET Core. It attempts to
keep the existing experimental .NET Core project files in line
with the msbuild files, but I expect further work to be required
for .NET Core, which has a different build/publication model.
@grpc-kokoro
Copy link

Thanks for your pull request. The automated tests will run as soon as one of the admins verifies this change is ok to test.

3 similar comments
@grpc-kokoro
Copy link

Thanks for your pull request. The automated tests will run as soon as one of the admins verifies this change is ok to test.

@grpc-kokoro
Copy link

Thanks for your pull request. The automated tests will run as soon as one of the admins verifies this change is ok to test.

@grpc-kokoro
Copy link

Thanks for your pull request. The automated tests will run as soon as one of the admins verifies this change is ok to test.

@ivannaranjo
Copy link

Can we change the way the Windows DLL is built to include an exports file? That will eliminate the issue you are having with the automatic names, you can export the functions with any name you want and isolating the calling code from this madness, see .def files here.

@@ -176,17 +178,18 @@ private static string GetArchitectureString()
// platform specific file name of the extension library
private static string GetNativeLibraryFilename()
{
string architecture = GetArchitectureString();

Choose a reason for hiding this comment

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

You do the platform check twice, inside of GetArchitectureString() and here. Why not just move the architecture check here and return the hardcoded name of the library? The String.Format doesn't really gain much in this scenario.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, GetArchitectureString only checks the 32/64-bit aspect; the only platform checks in here are for OS. Did you mean in GetPlatformString? I'm sure there are other ways of doing this, but I was aiming for minimal changes in this PR... this approach works, and the overhead is tiny and one-off.

@ivannaranjo
Copy link

Good heroic effort :). Sorry for the head aches.

@jskeet
Copy link
Contributor Author

jskeet commented Jul 15, 2016

We may be able to change how the DLL is built in the future, but I don't want to start messing with that right now. The workaround for guessing the name should be fine - if something changes that stops it working, it will break all the tests. It's an implementation detail that can definitely be changed later.

@jskeet
Copy link
Contributor Author

jskeet commented Jul 15, 2016

The "basic tests" failures don't look related to this change, as far as I can tell.

@apolcyn
Copy link
Contributor

apolcyn commented Jul 15, 2016

Seems to me too that the windows loading code could be simplified, but since its not missing out on anything that the DLLImport was getting (both covering stdcall names), this could be done separately. Made an issue for that here #7409

@apolcyn
Copy link
Contributor

apolcyn commented Jul 15, 2016

LGTM

@jskeet
Copy link
Contributor Author

jskeet commented Jul 15, 2016

@apolcyn: Could you merge, please? I don't have commit permissions on this project.

@apolcyn
Copy link
Contributor

apolcyn commented Jul 15, 2016

Sorry as I haven't been merging things yet, I'm waiting on someone else to merge now.

@kpayson64 kpayson64 merged commit 24ae8ea into grpc:v1.0.x Jul 15, 2016
@jtattermusch
Copy link
Contributor

@ivannaranjo

I believe the native DLLs on windows are built in the right way - there's two ways to export the symbols - using the .def file or using __declspec(dllexport) (see here:

#define GPR_EXPORT __declspec(dllexport)
). AFAIK, both have the same effect and without exporting the symbols, there [DllImport] attributes we were using until this PR was merged wouldn't work.

@ivannaranjo
Copy link

You can specify the name of the exported symbol in the def file with the =
syntax, which is the whole point. If you are going to import the symbols by
name you need to specify the name, don't count on the @ notation for the
names of the exported symbols.

On Mon, Jul 18, 2016 at 2:59 AM, Jan Tattermusch notifications@github.com
wrote:

@ivannaranjo https://github.com/ivannaranjo

I believe the native DLLs on windows are built in the right way - there's
two ways to export the symbols - using the .def file or using
__declspec(dllexport) (see here:

#define GPR_EXPORT __declspec(dllexport)
).
AFAIK, both have the same effect and without exporting the symbols, there
[DllImport] attributes we were using until this PR was merged wouldn't work.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#7401 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AJWmrAIQ0nHyzcXyATF-degrsf8gO3jZks5qWt37gaJpZM4JNG4y
.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants