Skip to content

Commit 95f123c

Browse files
committed
- Ensure python threads are always initialized, even if the main interpreter hasn't initialized them, otherwise attempting to dereference objects in finalizers called by the concurrent GC will fail.
- Dispose of PyObject instances after use in CreateSubType rather than wait for the GC - Don't try and dereference derived class instances after python has shutdown if called from the GC
1 parent 7e0226f commit 95f123c

File tree

3 files changed

+41
-14
lines changed

3 files changed

+41
-14
lines changed

pythonnet/src/runtime/classderived.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,12 @@ public static void Finalize(IPythonDerivedType obj)
519519

520520
// delete the python object in an asnyc task as we may not be able to acquire
521521
// the GIL immediately and we don't want to block the GC thread.
522-
var t = Task.Factory.StartNew(() => {
522+
var t = Task.Factory.StartNew(() =>
523+
{
524+
// If python's been terminated then there's nothing to do
525+
if (0 == Runtime.Py_IsInitialized())
526+
return;
527+
523528
IntPtr gs = Runtime.PyGILState_Ensure();
524529
try
525530
{

pythonnet/src/runtime/runtime.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,12 @@ internal static void Initialize() {
9696

9797
if (0 == Runtime.Py_IsInitialized())
9898
{
99-
Runtime.Py_Initialize();
100-
Runtime.PyEval_InitThreads();
99+
Runtime.Py_Initialize();
100+
}
101+
102+
if (0 == Runtime.PyEval_ThreadsInitialized())
103+
{
104+
Runtime.PyEval_InitThreads();
101105
}
102106

103107
#if (PYTHON32 || PYTHON33 || PYTHON34)
@@ -516,6 +520,11 @@ public unsafe static extern int
516520
internal unsafe static extern void
517521
PyEval_InitThreads();
518522

523+
[DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl,
524+
ExactSpelling=true, CharSet=CharSet.Ansi)]
525+
internal unsafe static extern int
526+
PyEval_ThreadsInitialized();
527+
519528
[DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl,
520529
ExactSpelling=true, CharSet=CharSet.Ansi)]
521530
internal unsafe static extern void

pythonnet/src/runtime/typemanager.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,20 +206,33 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr
206206
object assembly = null;
207207
object namespaceStr = null;
208208

209-
PyObject assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(String)));
210-
if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle))
209+
List<PyObject> disposeList = new List<PyObject>();
210+
try
211211
{
212-
PyObject pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle));
213-
if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(String), out assembly, false))
214-
throw new InvalidCastException("Couldn't convert __assembly__ value to string");
215-
}
212+
PyObject assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(String)));
213+
disposeList.Add(assemblyKey);
214+
if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle))
215+
{
216+
PyObject pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle));
217+
disposeList.Add(pyAssembly);
218+
if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(String), out assembly, false))
219+
throw new InvalidCastException("Couldn't convert __assembly__ value to string");
220+
}
216221

217-
PyObject namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__"));
218-
if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle))
222+
PyObject namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__"));
223+
disposeList.Add(namespaceKey);
224+
if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle))
225+
{
226+
PyObject pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle));
227+
disposeList.Add(pyNamespace);
228+
if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(String), out namespaceStr, false))
229+
throw new InvalidCastException("Couldn't convert __namespace__ value to string");
230+
}
231+
}
232+
finally
219233
{
220-
PyObject pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle));
221-
if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(String), out namespaceStr, false))
222-
throw new InvalidCastException("Couldn't convert __namespace__ value to string");
234+
foreach (PyObject o in disposeList)
235+
o.Dispose();
223236
}
224237

225238
// create the new managed type subclassing the base managed type

0 commit comments

Comments
 (0)