You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Python can't reliably re-initialize; new proposal for Unity's domain reload #957
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:
Don't Py_Finalize in Shutdown.
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.
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.
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.
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
Activity
benoithudson commentedon Sep 17, 2019
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.
benoithudson commentedon Sep 17, 2019
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#.
BadSingleton commentedon Sep 17, 2019
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
benoithudson commentedon Sep 17, 2019
Compare link (for ease of reading):
master...BadSingleton:issue-957-prototype
Not ready to make it a PR, we need much more testing.
filmor commentedon Sep 18, 2019
Thanks for the proposal and the work on this, I'll have a look into it.
amos402 commentedon Sep 18, 2019
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.
benoithudson commentedon Sep 18, 2019
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.
benoithudson commentedon Sep 18, 2019
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
returnwhich 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.
amos402 commentedon Sep 22, 2019
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
benoithudson commentedon Jan 19, 2021
This got fixed with the soft-shutdown work by @amos402 and @BadSingleton