Skip to content

Commit 1fae108

Browse files
committed
Reset gchead and refchain after shutdown
1 parent 05a1451 commit 1fae108

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

src/runtime/pythonengine.cs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.IO;
45
using System.Linq;
56
using System.Reflection;
@@ -12,11 +13,16 @@ namespace Python.Runtime
1213
/// </summary>
1314
public class PythonEngine : IDisposable
1415
{
16+
private const int NUM_GENERATIONS = 3;
17+
1518
private static DelegateManager delegateManager;
1619
private static bool initialized;
1720
private static IntPtr _pythonHome = IntPtr.Zero;
1821
private static IntPtr _programName = IntPtr.Zero;
1922
private static IntPtr _pythonPath = IntPtr.Zero;
23+
private static IntPtr _refChain = IntPtr.Zero;
24+
private static IntPtr[] _generations;
25+
2026

2127
public PythonEngine()
2228
{
@@ -168,6 +174,10 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
168174
initialized = true;
169175
Exceptions.Clear();
170176

177+
_generations = GetGCGenerations();
178+
#if PYTHON_WITH_PYDEBUG
179+
_refChain = GetRefChainHead();
180+
#endif
171181
if (setSysArgv)
172182
{
173183
Py.SetArgv(args);
@@ -303,6 +313,7 @@ public static void Shutdown()
303313
_pythonPath = IntPtr.Zero;
304314

305315
Runtime.Shutdown();
316+
ResetGC();
306317
initialized = false;
307318
}
308319
}
@@ -520,6 +531,62 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
520531
}
521532
}
522533
}
534+
535+
internal static void ResetGC()
536+
{
537+
ClearGC();
538+
#if PYTHON_WITH_PYDEBUG
539+
ResetRefChain();
540+
#endif
541+
}
542+
543+
private static void ClearGC()
544+
{
545+
Debug.Assert(_generations != null);
546+
foreach (IntPtr head in _generations)
547+
{
548+
// gc.gc_next
549+
Marshal.WriteIntPtr(head, 0, head);
550+
// gc.gc_prev
551+
Marshal.WriteIntPtr(head, IntPtr.Size, head);
552+
}
553+
}
554+
555+
private static IntPtr[] GetGCGenerations()
556+
{
557+
int GCHeadOffset = IntPtr.Size == 4 ? 24 : 32;
558+
IntPtr op = Runtime._PyObject_GC_New(Runtime.PyTypeType);
559+
Runtime.PyObject_GC_Track(op);
560+
IntPtr g = Runtime._Py_AS_GC(op);
561+
// According to _PyObjct_GC_TRACK and PyGC_Head strcture, g is the g->gc.gc_next
562+
// It also become the GEN_HEAD(0) now
563+
IntPtr[] gens = new IntPtr[NUM_GENERATIONS];
564+
for (int i = 0; i < NUM_GENERATIONS; i++)
565+
{
566+
gens[i] = g;
567+
g += GCHeadOffset;
568+
}
569+
Runtime.PyObject_GC_UnTrack(op);
570+
Runtime.PyObject_GC_Del(op);
571+
return gens;
572+
}
573+
574+
#if PYTHON_WITH_PYDEBUG
575+
private static void ResetRefChain()
576+
{
577+
Debug.Assert(_refChain != IntPtr.Zero);
578+
Marshal.WriteIntPtr(_refChain, ObjectOffset._ob_next, _refChain);
579+
Marshal.WriteIntPtr(_refChain, ObjectOffset._ob_prev, _refChain);
580+
}
581+
582+
private static IntPtr GetRefChainHead()
583+
{
584+
IntPtr op = Runtime._PyObject_GC_New(Runtime.PyBaseObjectType);
585+
IntPtr refchain = Marshal.ReadIntPtr(op, ObjectOffset._ob_prev);
586+
Runtime.PyObject_GC_Del(op);
587+
return refchain;
588+
}
589+
#endif
523590
}
524591

525592
public enum RunFlagType

src/runtime/runtime.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1575,7 +1575,7 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp)
15751575
internal static extern IntPtr _PyObject_GetDictPtr(IntPtr obj);
15761576

15771577
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1578-
internal static extern IntPtr PyObject_GC_New(IntPtr tp);
1578+
internal static extern IntPtr _PyObject_GC_New(IntPtr tp);
15791579

15801580
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
15811581
internal static extern void PyObject_GC_Del(IntPtr tp);
@@ -1600,6 +1600,10 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp)
16001600
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
16011601
internal static extern void PyMem_Free(IntPtr ptr);
16021602

1603+
internal static IntPtr _Py_AS_GC(IntPtr op)
1604+
{
1605+
return op - IntPtr.Size;
1606+
}
16031607

16041608
//====================================================================
16051609
// Python exception API

0 commit comments

Comments
 (0)