Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ecf3237

Browse files
committed
Stop PythonDerivedType.Finalize from trying to delete any Python objects
once Py_Finalize has started.
1 parent f0d0aeb commit ecf3237

File tree

4 files changed

+62
-18
lines changed

4 files changed

+62
-18
lines changed

pythonnet/src/runtime/classderived.cs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -531,29 +531,45 @@ public static void Finalize(IPythonDerivedType obj)
531531
FieldInfo fi = obj.GetType().GetField("__pyobj__");
532532
CLRObject self = (CLRObject)fi.GetValue(obj);
533533

534-
// delete the python object in an asnyc task as we may not be able to acquire
535-
// the GIL immediately and we don't want to block the GC thread.
536-
var t = Task.Factory.StartNew(() =>
534+
// If python's been terminated then just free the gchandle.
535+
lock (Runtime.IsFinalizingLock)
537536
{
538-
// If python's been terminated then there's nothing to do
539-
if (0 == Runtime.Py_IsInitialized())
540-
return;
541-
542-
IntPtr gs = Runtime.PyGILState_Ensure();
543-
try
537+
if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing)
544538
{
545-
// the C# object is being destroyed which must mean there are no more
546-
// references to the Python object as well so now we can dealloc the
547-
// python object.
548-
IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle));
549-
if (dict != IntPtr.Zero)
550-
Runtime.Decref(dict);
551-
Runtime.PyObject_GC_Del(self.pyHandle);
552539
self.gcHandle.Free();
540+
return;
553541
}
554-
finally
542+
}
543+
544+
// delete the python object in an asnyc task as we may not be able to acquire
545+
// the GIL immediately and we don't want to block the GC thread.
546+
var t = Task.Factory.StartNew(() =>
547+
{
548+
lock (Runtime.IsFinalizingLock)
555549
{
556-
Runtime.PyGILState_Release(gs);
550+
// If python's been terminated then just free the gchandle.
551+
if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing)
552+
{
553+
self.gcHandle.Free();
554+
return;
555+
}
556+
557+
IntPtr gs = Runtime.PyGILState_Ensure();
558+
try
559+
{
560+
// the C# object is being destroyed which must mean there are no more
561+
// references to the Python object as well so now we can dealloc the
562+
// python object.
563+
IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle));
564+
if (dict != IntPtr.Zero)
565+
Runtime.Decref(dict);
566+
Runtime.PyObject_GC_Del(self.pyHandle);
567+
self.gcHandle.Free();
568+
}
569+
finally
570+
{
571+
Runtime.PyGILState_Release(gs);
572+
}
557573
}
558574
});
559575
}

pythonnet/src/runtime/moduleobject.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ public static String[] ListAssemblies(bool verbose)
442442
return names;
443443
}
444444

445+
[ModuleFunctionAttribute()]
446+
public static int _AtExit()
447+
{
448+
return Runtime.AtExit();
449+
}
450+
445451
}
446452

447453
}

pythonnet/src/runtime/pythonengine.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ public static void Initialize() {
117117
Runtime.Initialize();
118118
initialized = true;
119119
Exceptions.Clear();
120+
121+
// register the atexit callback (this doesn't use Py_AtExit as the C atexit
122+
// callbacks are called after python is fully finalized but the python ones
123+
// are called while the python engine is still running).
124+
string code =
125+
"import atexit, clr\n" +
126+
"atexit.register(clr._AtExit)\n";
127+
PyObject r = PythonEngine.RunString(code);
128+
if (r != null)
129+
r.Dispose();
120130
}
121131
}
122132

pythonnet/src/runtime/runtime.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ public class Runtime {
8484
#error You must define one of PYTHON23 to PYTHON34
8585
#endif
8686

87+
// set to true when python is finalizing
88+
internal static Object IsFinalizingLock = new Object();
89+
internal static bool IsFinalizing = false;
90+
8791
internal static bool wrap_exceptions;
8892
internal static bool is32bit;
8993

@@ -232,6 +236,14 @@ internal static void Shutdown() {
232236
Py_Finalize();
233237
}
234238

239+
// called *without* the GIL aquired by clr._AtExit
240+
internal static int AtExit() {
241+
lock (IsFinalizingLock) {
242+
IsFinalizing = true;
243+
}
244+
return 0;
245+
}
246+
235247
internal static IntPtr Py_single_input = (IntPtr)256;
236248
internal static IntPtr Py_file_input = (IntPtr)257;
237249
internal static IntPtr Py_eval_input = (IntPtr)258;

0 commit comments

Comments
 (0)