Skip to content

Crashes on .NET Core 3 and 2 on Linux #967

Closed

Description

@emilmuller

Can't make this work on Linux Mint 19 (Ubuntu 18.04).
Compiled ReleaseMonoPy3 with XPLAT;;PYTHON3;PYTHON37;UCS4;MONO_LINUX;PYTHON_WITH_PYMALLOC.
It seems to crash on Py.GIL():

using (Py.GIL())
{
    dynamic np = Py.Import("numpy");
    Console.WriteLine(np.cos(np.pi * 2));

    dynamic sin = np.sin;
    Console.WriteLine(sin(5));

    double c = np.cos(5) + sin(5);
    Console.WriteLine(c);

    dynamic a = np.array(new List<float> { 1, 2, 3 });
    Console.WriteLine(a.dtype);

    dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
    Console.WriteLine(b.dtype);

    Console.WriteLine(a * b);
    Console.ReadKey();
}

Upon further investigation, it seems to crash in InitializePlatformData(), as

IntPtr platformModule = PyImport_ImportModule("platform"); // Becomes NULL pointer

Running this line immediately after setting platformModule = ...

Console.WriteLine(new PythonException().Message);

yields:

ModuleNotFoundError : No module named '_posixsubprocess'

I also tried making sure the python home and path variables are correctly set:

PythonEngine.PythonHome = @"/usr/local/bin/python3.7";
PythonEngine.PythonPath = @"/usr/local/lib/python3.7";

I have no problem importing _posixsubprocess module when running the python3.7 binary from the terminal.

Activity

  1. filmor commented on Oct 10, 2019

    @filmor
    Member

    Please verify your installation. If you already get a PythonException, that means that libpython has been found and is loaded, so not finding modules is a pure configuration problem that has nothing to do with Python.NET and would affect any other embedding scenario as well. Check what python -c "import sys; print(sys.path) spits out and try setting that as your PythonPath (separated by : on Unix and ; on Windows).

  2. Jeff17Robbins commented on Dec 15, 2019

    @Jeff17Robbins
    Contributor

    @filmor I respectfully disagree about this issue having nothing to do with Python.NET. Please see my comment in #891.

    I think the potential source of confusion is that the [DllImport] that finds and loads libpython does not set the RTLD_GLOBAL flag on its dlopen() call. And, furthermore, some Linuxes like Debian explicitly "does not link extensions to libpython".

    [I assume that this is why runtime.cs takes such trouble to find out the OperatingSystemName and MachineName in InitializePlatformData, precisely in order to explictly load libpython with said RTLD_GLOBAL flag?]

    So when platform.py changed to using subprocess.py, which on Linux attempts to import a C extension library _posixsubprocess, there's a crash.

    This crash is, I think, precisely due to Python.NET running C Python code (importing platform.py) in a"semi-initialized state" inside its Initialize before it has done that explicit Load of libpython with RTLD_GLOBAL.

    I don't think this crash reflects a configuration problem. I think this crash reflects on why runtime.cs needs to reload libpython in the first place!

    For .NET Core 3.x, we could run this code instead of using platform.py, and get our required strings quicker, without this crash. For example, with code like this (tested on Debian 10 .NET Core 3.1):

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    OperatingSystemName = "Windows";
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                    OperatingSystemName = "Darwin";
                else
                    OperatingSystemName = "Linux";
    
                if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
                    MachineName = "x86";
                else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
                    MachineName = "x86_64";
                else
                    MachineName = "Other";
    

    PS, I believe this analysis explains issue #946 also.

  3. filmor commented on Dec 15, 2019

    @filmor
    Member

    @Jeff17Robbins Well, Python.NET didn't decide to default to RTLD_LOCAL on .NET Core in DllImport (which I think is an unfortunate choice).

    Nevertheless, you are completely right that this whole InitializePlatformData dance can be avoided by using RuntimeInformation (I think there is even a comment in our code that we could do that), but for this we need to "officially" update to .NET >4.7.1. There is a ticket for this (#897) and we'll decide this Tuesday, but as far as I can see everyone is in favour.

    We wouldn't even need to assign our own values here anymore, we can just use the RuntimeInformation data directly.

  4. Jeff17Robbins commented on Dec 16, 2019

    @Jeff17Robbins
    Contributor

    @filmor Conceivably, for .NET Core 3.1 anyway, we could override that unfortunate choice using System.Runtime.InteropServices.DllImportResolver. I'm not clear on how to get a call to this to run before all those [DllImport]s, but if you have any ideas about that, I'd be happy to give it a try. The resolver can call dlopen with the RTLD_GLOBAL flag.

    It's not a solution for .NET Framework 4.7.x.

    But it could tidy things up for .NET Core on Linux and remove the need for the second call to dlopen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions