Skip to content

Commit 1cf616d

Browse files
authored
Merge pull request #1712 from losttech/bugs/ShutdownSegFault
On shutdown from Python release all slot holders
2 parents 0e57cdd + 58bd58c commit 1cf616d

File tree

7 files changed

+37
-24
lines changed

7 files changed

+37
-24
lines changed

pythonnet.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F
2525
EndProject
2626
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}"
2727
ProjectSection(SolutionItems) = preProject
28+
.github\workflows\ARM.yml = .github\workflows\ARM.yml
2829
.github\workflows\main.yml = .github\workflows\main.yml
2930
.github\workflows\nuget-preview.yml = .github\workflows\nuget-preview.yml
3031
EndProjectSection

pythonnet/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def set_runtime(runtime):
1616

1717

1818
def set_default_runtime() -> None:
19-
if sys.platform == 'win32':
19+
if sys.platform == "win32":
2020
set_runtime(clr_loader.get_netfx())
2121
else:
2222
set_runtime(clr_loader.get_mono())
@@ -36,22 +36,23 @@ def load():
3636
set_default_runtime()
3737

3838
dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll")
39-
39+
4040
_LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path)
4141

4242
func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"]
43-
if func(''.encode("utf8")) != 0:
43+
if func(b"") != 0:
4444
raise RuntimeError("Failed to initialize Python.Runtime.dll")
4545

4646
import atexit
47+
4748
atexit.register(unload)
4849

4950

5051
def unload():
5152
global _RUNTIME
5253
if _LOADER_ASSEMBLY is not None:
5354
func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"]
54-
if func(b"") != 0:
55+
if func(b"full_shutdown") != 0:
5556
raise RuntimeError("Failed to call Python.NET shutdown")
5657

5758
if _RUNTIME is not None:

src/runtime/Runtime.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ private static string GetDefaultDllName(Version version)
5454
}
5555

5656
private static bool _isInitialized = false;
57-
5857
internal static bool IsInitialized => _isInitialized;
58+
private static bool _typesInitialized = false;
59+
internal static bool TypeManagerInitialized => _typesInitialized;
5960
internal static readonly bool Is32Bit = IntPtr.Size == 4;
6061

6162
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
@@ -151,6 +152,7 @@ internal static void Initialize(bool initSigs = false)
151152
ClassManager.Reset();
152153
ClassDerivedObject.Reset();
153154
TypeManager.Initialize();
155+
_typesInitialized = true;
154156

155157
// Initialize modules that depend on the runtime class.
156158
AssemblyManager.Initialize();
@@ -273,6 +275,7 @@ internal static void Shutdown()
273275
NullGCHandles(ExtensionType.loadedExtensions);
274276
ClassManager.RemoveClasses();
275277
TypeManager.RemoveTypes();
278+
_typesInitialized = false;
276279

277280
MetaType.Release();
278281
PyCLRMetaType.Dispose();
@@ -293,9 +296,10 @@ internal static void Shutdown()
293296
Finalizer.Shutdown();
294297
InternString.Shutdown();
295298

299+
ResetPyMembers();
300+
296301
if (!HostedInPython)
297302
{
298-
ResetPyMembers();
299303
GC.Collect();
300304
GC.WaitForPendingFinalizers();
301305
PyGILState_Release(state);
@@ -312,7 +316,6 @@ internal static void Shutdown()
312316
}
313317
else
314318
{
315-
ResetPyMembers();
316319
PyGILState_Release(state);
317320
}
318321
}

src/runtime/TypeManager.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,21 @@ internal static void Initialize()
5151

5252
internal static void RemoveTypes()
5353
{
54-
foreach (var type in cache.Values)
54+
if (Runtime.HostedInPython)
5555
{
56-
if (Runtime.HostedInPython
57-
&& _slotsHolders.TryGetValue(type, out var holder))
56+
foreach (var holder in _slotsHolders)
5857
{
5958
// If refcount > 1, it needs to reset the managed slot,
6059
// otherwise it can dealloc without any trick.
61-
if (Runtime.Refcount(type) > 1)
60+
if (holder.Key.Refcount > 1)
6261
{
63-
holder.ResetSlots();
62+
holder.Value.ResetSlots();
6463
}
6564
}
65+
}
66+
67+
foreach (var type in cache.Values)
68+
{
6669
type.Dispose();
6770
}
6871
cache.Clear();
@@ -507,7 +510,7 @@ internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder)
507510
{
508511
throw PythonException.ThrowLastAsClrException();
509512
}
510-
513+
511514
BorrowedReference dict = Util.ReadRef(type, TypeOffset.tp_dict);
512515
using (var mod = Runtime.PyString_FromString("clr._internal"))
513516
Runtime.PyDict_SetItemString(dict, "__module__", mod.Borrow());
@@ -726,6 +729,7 @@ internal static void CopySlot(BorrowedReference from, BorrowedReference to, int
726729

727730
internal static SlotsHolder CreateSlotsHolder(PyType type)
728731
{
732+
type = new PyType(type);
729733
var holder = new SlotsHolder(type);
730734
_slotsHolders.Add(type, holder);
731735
return holder;
@@ -828,6 +832,7 @@ public void ResetSlots()
828832
var metatype = Runtime.PyObject_TYPE(Type);
829833
ManagedType.TryFreeGCHandle(Type, metatype);
830834
}
835+
Runtime.PyType_Modified(Type);
831836
}
832837

833838
public static IntPtr GetDefaultSlot(int offset)

src/runtime/Types/ClassDerived.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ protected override NewReference NewObjectToPython(object obj, BorrowedReference
5959
// Decrement the python object's reference count.
6060
// This doesn't actually destroy the object, it just sets the reference to this object
6161
// to be a weak reference and it will be destroyed when the C# object is destroyed.
62-
if (!self.IsNull())
63-
{
64-
Runtime.XDecref(self.Steal());
65-
}
62+
Runtime.XDecref(self.Steal());
6663

6764
return Converter.ToPython(obj, type.Value);
6865
}
@@ -942,13 +939,16 @@ internal static void Finalize(IntPtr derived)
942939

943940
var type = Runtime.PyObject_TYPE(@ref.Borrow());
944941

945-
// rare case when it's needed
946-
// matches correspdonging PyObject_GC_UnTrack
947-
// in ClassDerivedObject.tp_dealloc
948-
Runtime.PyObject_GC_Del(@ref.Steal());
942+
if (!Runtime.HostedInPython || Runtime.TypeManagerInitialized)
943+
{
944+
// rare case when it's needed
945+
// matches correspdonging PyObject_GC_UnTrack
946+
// in ClassDerivedObject.tp_dealloc
947+
Runtime.PyObject_GC_Del(@ref.Steal());
949948

950-
// must decref our type
951-
Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress()));
949+
// must decref our type
950+
Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress()));
951+
}
952952
}
953953

954954
internal static FieldInfo? GetPyObjField(Type type) => type.GetField(PyObjName, PyObjFlags);

src/runtime/Types/ModuleObject.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ public static Assembly AddReference(string name)
542542
/// <returns>The Type object</returns>
543543

544544
[ModuleFunction]
545-
[ForbidPythonThreads]
546545
public static Type GetClrType(Type type)
547546
{
548547
return type;

tests/test_engine.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ def test_run_string():
4141
assert sys.multiline_worked == 1
4242

4343
PythonEngine.ReleaseLock()
44+
45+
def test_leak_type():
46+
import clr
47+
sys._leaked_intptr = clr.GetClrType(System.IntPtr)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy