← 输入框|github.com|刷新
Skip to content

Python can't reliably re-initialize; new proposal for Unity's domain reload #957

Closed

Description

@benoithudson

Problem: bpo-37931 demonstrates that even Python itself can't re-initialize on all platforms without crashing. Major third-party packages including numpy and PySide also crash when you Py_Initialize after a Py_Finalize and reimport the package.

The reason we Py_Finalize in Shutdown is because otherwise, the python side has live objects that have function pointers into C#. If the C# domain is reloaded (which frequently happens in the Unity3d editor) then those pointers are pointing to garbage. So, calling any C#-implemented function on those python objects causes the PC to jump to garbage and thus "undefined behaviour" (usually a crash).

New proposed solution:

  1. Don't Py_Finalize in Shutdown.
  2. Find all the function pointers from Python to C# and set them to a reasonable value so that they have a reasonably pythonic exception rather than jumping to garbage.

@BadSingleton on our team has prototyped this. It seems the only place we jump to C# is in the tp_* slots of a type. Setting them to null, as most slots are set in Python's noddy example, seems to work fine (except for the slots for garbage collection, which must point to something, so we still need that silly native code page for now).

Example: we have a proxy to a C# object in python and we call ToString() on it. Before domain reload, Python calls the tp_getattro slot which is implemented in C# and it does the reflection to find ToString, and then it calls tp_call which is implemented in C# and it calls the function. After domain reload, Python sees that tp_getattro is null so it uses the default getattr implementation, which has never heard of ToString, so it throws an AttributeError. If we cached ToString on the python side, then Python looks at the tp_call slot which is null and throws a TypeError because it's not callable.

Activity

  1. benoithudson commented on Sep 17, 2019

    @benoithudson
    ContributorAuthor

    The MVP here is just: don't crash.

    A slight improvement is to see if it's easy to get an "underlying C# object has been deleted" exception, so the PySide crew feels right at home.

  2. benoithudson commented on Sep 17, 2019

    @benoithudson
    ContributorAuthor

    I'm putting this up as a point of discussion for next week's meeting.

    I want to make sure there's no other ways to jump from python into C#.

  3. BadSingleton commented on Sep 17, 2019

    @BadSingleton
    Contributor

    Speaking of prototype: https://github.com/BadSingleton/pythonnet/tree/issue-957-prototype
    It is a fairly simple one; a proof of concept for a simple repro case to see if it was doable

  4. benoithudson commented on Sep 17, 2019

    @benoithudson
    ContributorAuthor

    Compare link (for ease of reading):
    master...BadSingleton:issue-957-prototype

    Not ready to make it a PR, we need much more testing.

  5. filmor commented on Sep 18, 2019

    @filmor
    Member

    Thanks for the proposal and the work on this, I'll have a look into it.

  6. amos402 commented on Sep 18, 2019

    @amos402
    Member

    The memory leaking is still large if you just remove clr modules from sys.modules. Also it didn't remove other modules, if a Python module 'A' import a clr module, after reload domain, the A's member wouldn't change it still point to the old value.
    I think it's not a best choice to skip Py_Finalize at all, it should be an option.

  7. benoithudson commented on Sep 18, 2019

    @benoithudson
    ContributorAuthor

    I see memory leaks as orthogonal to this issue. They're also an important thing but even fixing all the memory leaks won't fix the crashes.

  8. benoithudson commented on Sep 18, 2019

    @benoithudson
    ContributorAuthor

    Expanding on that:

    • If we don't leak any memory but we crash, then we're not helping the user.

    • If we leak lots of memory but don't crash, the user can mostly do their work but once in a while will run out of memory and be forced to restart their application.

    • If we leak no memory and don't crash, the user is 100% happy.

    So definitely we want to figure out how to avoid leaking (currently IIRC all the C# finalizers just return which needs to be fixed), and we should have an issue to track that, and ideally also some testing in CI so we can track our progress like with code coverage.

    But also we need to avoid crashing, which is what this issue focuses on.

  9. amos402 commented on Sep 22, 2019

    @amos402
    Member

    I fixed all memory leakings those I can found, although still have a little leaking, I'm not sure where those come from.
    图片
    A loop that create a domain call PyQt and create a qt window, then close it after 100ms.
    sample code
    master...amos402:soft-shutdown

  10. benoithudson commented on Jan 19, 2021

    @benoithudson
    ContributorAuthor

    This got fixed with the soft-shutdown work by @amos402 and @BadSingleton

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