8000 GC-related WIP · pythonnet/pythonnet@48c0dfc · GitHub
[go: up one dir, main page]

Skip to content

Commit 48c0dfc

Browse files
committed
GC-related WIP
1 parent e31f7ba commit 48c0dfc

21 files changed

+409
-482
lines changed

src/embed_tests/TestRuntime.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ public static void Py_IsInitializedValue()
3232
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
3333
}
3434

35+
[Test]
36+
public static void IterAcrossRuns()
37+
{
38+
Runtime.Runtime.Py_Initialize();
39+
BorrowedReference builtins = Runtime.Runtime.PyEval_GetBuiltins();
40+
BorrowedReference iter = Runtime.Runtime.PyDict_GetItemString(builtins, "iter");
41+
42+
using var ownedIter = new NewReference(iter);
43+
Runtime.Runtime.Py_Finalize();
44+
45+
Runtime.Runtime.Py_Initialize();
46+
ownedIter.Dispose();
47+
Runtime.Runtime.Py_Finalize();
48+
}
49+
3550
[Test]
3651
public static void RefCountTest()
3752
{

src/runtime/Util.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,58 +28,68 @@ internal static class Util
2828
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2929
internal static int ReadInt32(BorrowedReference ob, int offset)
3030
{
31+
Debug.Assert(offset >= 0);
3132
return Marshal.ReadInt32(ob.DangerousGetAddress(), offset);
3233
}
3334
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3435
internal static long ReadInt64(BorrowedReference ob, int offset)
3536
{
37+
Debug.Assert(offset >= 0);
3638
return Marshal.ReadInt64(ob.DangerousGetAddress(), offset);
3739
}
3840

3941
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4042
internal unsafe static T* ReadPtr<T>(BorrowedReference ob, int offset)
4143
where T: unmanaged
4244
{
45+
Debug.Assert(offset >= 0);
4346
IntPtr ptr = Marshal.ReadIntPtr(ob.DangerousGetAddress(), offset);
4447
return (T*)ptr;
4548
}
4649

4750
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4851
internal unsafe static IntPtr ReadIntPtr(BorrowedReference ob, int offset)
4952
{
53+
Debug.Assert(offset >= 0);
5054
return Marshal.ReadIntPtr(ob.DangerousGetAddress(), offset);
5155
}
5256

5357
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5458
internal unsafe static BorrowedReference ReadRef(BorrowedReference @ref, int offset)
5559
{
60+
Debug.Assert(offset >= 0);
5661
return new BorrowedReference(ReadIntPtr(@ref, offset));
5762
}
5863

5964
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6065
internal static void WriteInt32(BorrowedReference ob, int offset, int value)
6166
{
67+
Debug.Assert(offset >= 0);
6268
Marshal.WriteInt32(ob.DangerousGetAddress(), offset, value);
6369
}
6470
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6571
internal static void WriteInt64(BorrowedReference ob, int offset, long value)
6672
{
73+
Debug.Assert(offset >= 0);
6774
Marshal.WriteInt64(ob.DangerousGetAddress(), offset, value);
6875
}
6976
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7077
internal unsafe static void WriteIntP 10000 tr(BorrowedReference ob, int offset, IntPtr value)
7178
{
79+
Debug.Assert(offset >= 0);
7280
Marshal.WriteIntPtr(ob.DangerousGetAddress(), offset, value);
7381
}
7482
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7583
internal unsafe static void WriteRef(BorrowedReference ob, int offset, in StolenReference @ref)
7684
{
85+
Debug.Assert(offset >= 0);
7786
Marshal.WriteIntPtr(ob.DangerousGetAddress(), offset, @ref.DangerousGetAddress());
7887
}
7988

8089
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8190
internal unsafe static void WriteNullableRef(BorrowedReference ob, int offset, in StolenReference @ref)
8291
{
92+
Debug.Assert(offset >= 0);
8393
Marshal.WriteIntPtr(ob.DangerousGetAddress(), offset, @ref.DangerousGetAddressOrNull());
8494
}
8595

src/runtime/arrayobject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
143143
public new static NewReference mp_subscript(BorrowedReference ob, BorrowedReference idx)
144144
{
145145
var obj = (CLRObject)GetManagedObject(ob)!;
146-
var arrObj = (ArrayObject)GetManagedObjectType(ob)!;
146+
var arrObj = (ArrayObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!;
147147
if (!arrObj.type.Valid)
148148
{
149149
return Exceptions.RaiseTypeError(arrObj.type.DeletedMessage);

src/runtime/classbase.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -337,33 +337,37 @@ public static NewReference tp_repr(BorrowedReference ob)
337337
/// </summary>
338338
public static void tp_dealloc(NewReference lastRef)
339339
{
340-
GCHandle? gcHandle = TryGetGCHandle(lastRef.Borrow());
340+
Runtime.PyGC_ValidateLists();
341+
Runtime.PyObject_GC_UnTrack(lastRef.Borrow());
341342

342-
tp_clear(lastRef.Borrow());
343+
CallClear(lastRef.Borrow());
343344

344345
IntPtr addr = lastRef.DangerousGetAddress();
345346
bool deleted = CLRObject.reflectedObjects.Remove(addr);
346347
Debug.Assert(deleted);
347348

348-
Runtime.PyObject_GC_UnTrack(lastRef.Borrow());
349-
Runtime.PyObject_GC_Del(lastRef.Steal());
350-
351-
gcHandle?.Free();
349+
DecrefTypeAndFree(lastRef.Steal());
350+
Runtime.PyGC_ValidateLists();
352351
}
353352

354353
public static int tp_clear(BorrowedReference ob)
355354
{
355+
Runtime.PyGC_ValidateLists();
356+
GCHandle? gcHandle = TryGetGCHandle(ob);
357+
gcHandle?.Free();
358+
356359
int baseClearResult = BaseUnmanagedClear(ob);
357360
if (baseClearResult != 0)
358361
{
359362
return baseClearResult;
360363
}
361364

362365
ClearObjectDict(ob);
366+
Runtime.PyGC_ValidateLists();
363367
return 0;
364368
}
365369

366-
static unsafe int BaseUnmanagedClear(BorrowedReference ob)
370+
internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
367371
{
368372
var type = Runtime.PyObject_TYPE(ob);
369373
var unmanagedBase = GetUnmanagedBaseType(type);
@@ -374,10 +378,10 @@ static unsafe int BaseUnmanagedClear(BorrowedReference ob)
374378
}
375379
var clear = (delegate* unmanaged[Cdecl]<BorrowedReference, int>)clearPtr;
376380

377-
bool usesSubtypeClear = clearPtr == Util.ReadIntPtr(Runtime.CLRMetaType, TypeOffset.tp_clear);
381+
bool usesSubtypeClear = clearPtr == TypeManager.subtype_clear;
378382
if (usesSubtypeClear)
379383
{
380-
// workaround for https://bugs.python.org/issue45266
384+
// workaround for https://bugs.python.org/issue45266 (subtype_clear)
381385
using var dict = Runtime.PyObject_GenericGetDict(ob);
382386
if (Runtime.PyMapping_HasKey(dict.Borrow(), PyIdentifier.__clear_reentry_guard__) != 0)
383387
return 0;

src/runtime/classderived.cs

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Collections.Generic;
43
using System.ComponentModel;
54
using System.Diagnostics;
65
using System.Linq;
76
using System.Reflection;
87
using System.Reflection.Emit;
98
using System.Runtime.InteropServices;
10-
using System.Threading;
119

1210
using Python.Runtime.Native;
1311

@@ -71,6 +69,7 @@ internal ClassDerivedObject(Type tp) : base(tp)
7169

7270
public new static void tp_dealloc(NewReference ob)
7371
{
72+
Runtime.PyGC_ValidateLists();
7473
var self = (CLRObject)GetManagedObject(ob.Borrow())!;
7574

7675
// don't let the python GC destroy this object
@@ -84,6 +83,7 @@ internal ClassDerivedObject(Type tp) : base(tp)
8483
GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak);
8584
SetGCHandle(ob.Borrow(), gc);
8685
oldHandle.Free();
86+
Runtime.PyGC_ValidateLists();
8787
}
8888

8989
/// <summary>
@@ -800,6 +800,8 @@ public static void InvokeSetProperty<T>(IPythonDerivedType obj, string propertyN
800800

801801
public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, object[] args)
802802
{
803+
Debug.Assert(Runtime.PyGILState_Check() != 0);
804+
803805
// call the base constructor
804806
obj.GetType().InvokeMember(origCtorName,
805807
BindingFlags.InvokeMethod,
@@ -833,58 +835,12 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
833835
}
834836
}
835837

836-
static readonly ConcurrentQueue<IntPtr> finalizeQueue = new();
837-
static readonly Lazy<Thread> derivedFinalizer = new(() =>
838-
{
839-
var thread = new Thread(DerivedFinalizerMain)
840-
{
841-
IsBackground = true,
842-
};
843-
thread.Start();
844-
return thread;
845-
}, LazyThreadSafetyMode.ExecutionAndPublication);
846-
847-
static void DerivedFinalizerMain()
848-
{
849-
while (true)
850-
{
851-
if (0 == Runtime.Py_IsInitialized())
852-
{
853-
Thread.Sleep(millisecondsTimeout: 1000);
854-
}
855-
856-
PyGILState gs = Runtime.PyGILState_Ensure();
857-
try
858-
{
859-
while (finalizeQueue.Count > 0)
860-
{
861-
finalizeQueue.TryDequeue(out IntPtr obj);
862-
var @ref = new BorrowedReference(obj);
863-
GCHandle gcHandle = ManagedType.GetGCHandle(@ref);
864-
865-
bool deleted = CLRObject.reflectedObjects.Remove(obj);
866-
Debug.Assert(deleted);
867-
Runtime.PyObject_GC_Del(@ref);
868-
869-
gcHandle.Free();
870-
}
871-
872-
}
873-
finally
874-
{
875-
Runtime.PyGILState_Release(gs);
876-
}
877-
}
878-
}
879838
public static void PyFinalize(IPythonDerivedType obj)
880839
{
881840
// the C# object is being destroyed which must mean there are no more
882841
// references to the Python object as well
883842
var self = GetPyObj(obj).DangerousGetAddress();
884-
finalizeQueue.Enqueue(self);
885-
SetPyObj(obj, null);
886-
887-
GC.KeepAlive(derivedFinalizer.Value);
843+
Finalizer.Instance.AddDerivedFinalizedObject(ref self);
888844
}
889845

890846
internal static BorrowedReference GetPyObj(IPythonDerivedType obj)

src/runtime/classmanager.cs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,6 @@ internal static void RemoveClasses()
6363
cache.Clear();
6464
}
6565

66-
private static int TraverseTypeClear(BorrowedReference ob, IntPtr arg)
67-
{
68-
var visited = (HashSet<IntPtr>)GCHandle.FromIntPtr(arg).Target;
69-
if (!visited.Add(ob.DangerousGetAddressOrNull()))
70-
{
71-
return 0;
72-
}
73-
var clrObj = ManagedType.GetManagedObject(ob);
74-
if (clrObj != null)
75-
{
76-
BorrowedReference tp = Runtime.PyObject_TYPE(ob);
77-
ManagedType.CallTypeTraverse(ob, tp, TraverseTypeClear, arg);
78-
ManagedType.CallTypeClear(ob, tp);
79-
}
80-
return 0;
81-
}
82-
8366
internal static ClassManagerState SaveRuntimeData()
8467
{
8568
var contexts = new Dictionary<ReflectedClrType, InterDomainContext>();

src/runtime/constructorbinding.cs

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -147,24 +147,15 @@ public static NewReference tp_repr(BorrowedReference ob)
147147
return new NewReference(self.repr);
148148
}
149149

150-
protected override void Clear(BorrowedReference ob)
151-
{
152-
Runtime.Py_CLEAR(ref this.repr);
153-
base.Clear(ob);
154-
}
155-
156150
public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg)
157151
{
158-
var self = (ConstructorBinding)GetManagedObject(ob)!;
159-
int res = PyVisit(self.typeToCreate, visit, arg);
160-
if (res != 0) return res;
152+
var self = (ConstructorBinding?)GetManagedObject(ob);
153+
if (self is null) return 0;
161154

162-
if (self.repr is not null)
163-
{
164-
res = PyVisit(self.repr, visit, arg);
165-
if (res != 0) return res;
166-
}
167-
return 0;
155+
Runtime.PyGC_ValidateLists();
156+
int res = PyVisit(self.typeToCreate, visit, arg);
157+
Runtime.PyGC_ValidateLists();
158+
return res;
168159
}
169160
}
170161

@@ -241,24 +232,15 @@ public static NewReference tp_repr(BorrowedReference ob)
241232
return new NewReference(self.repr);
242233
}
243234

244-
protected override void Clear(BorrowedReference ob)
245-
{
246-
Runtime.Py_CLEAR(ref this.repr);
247-
base.Clear(ob);
248-
}
249-
250235
public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg)
251236
{
252-
var self = (BoundContructor)GetManagedObject(ob)!;
253-
int res = PyVisit(self.typeToCreate, visit, arg);
254-
if (res != 0) return res;
237+
var self = (BoundContructor?)GetManagedObject(ob);
238+
if (self is null) return 0;
255239

256-
if (self.repr is not null)
257-
{
258-
res = PyVisit(self.repr, visit, arg);
259-
if (res != 0) return res;
260-
}
261-
return 0;
240+
Runtime.PyGC_ValidateLists();
241+
int res = PyVisit(self.typeToCreate, visit, arg);
242+
Runtime.PyGC_ValidateLists();
243+
return res;
262244
}
263245
}
264246
}

0 commit comments

Comments
 (0)
0