Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7ed0c7a
WIP
lostmsu Oct 24, 2021
d6a853f
avoid generating and handling useless SerializationException when May…
lostmsu Oct 28, 2021
b0c25c1
finalizer does not attempt to finalize objects when runtime is shut down
lostmsu Oct 28, 2021
5ca474a
PyType Dict and MRO properties to assist debugging
lostmsu Oct 28, 2021
48078b3
WIP 2
lostmsu Oct 28, 2021
a624dd8
fixed PyObject disposal crashing when runtime is still finalizing
lostmsu Oct 29, 2021
e7ab071
arrays: use 64 bit indexing, and avoid first chance .NET exceptions o…
lostmsu Oct 29, 2021
cbe1dd2
refactored conditional ClassBase slot initialization
lostmsu Oct 29, 2021
d5f1c48
removed DisposePythonWrappersForClrTypes
lostmsu Oct 29, 2021
74d87c5
simplified outdated condition in ClassBase.tp_clear
lostmsu Oct 29, 2021
82d6c33
sprinkled a few DebuggerHidden to make debugging easier
lostmsu Oct 29, 2021
eeebcd7
fixed derived classes not inheriting slots correctly
lostmsu Oct 29, 2021
8ee8d3d
remove unused TypeManager._slotImpls
lostmsu Oct 29, 2021
1a4ada7
fixed TestRuntime not building in Release mode
lostmsu Oct 30, 2021
a610aa3
can't really clear managed references to Python objects from ManagedT…
lostmsu Oct 30, 2021
03f32cb
PythonException is serializable
lostmsu Oct 30, 2021
b1c9f5b
EventObject no longer used for static events. EventBinding is constru…
lostmsu Oct 30, 2021
cb4bb9a
use a special class to stub .NET types that no longer exist after a d…
lostmsu Nov 2, 2021
652f946
make EventHandlerCollection serializable
lostmsu Nov 2, 2021
84db670
fixed MaybeMemberInfo always failing for properties
lostmsu Nov 2, 2021
56fafe3
fixed construct_removed_class domain reload test case
lostmsu Nov 2, 2021
d33dcdd
domain reload test runner can run test by index
lostmsu Nov 2, 2021
b737e10
minor docs change
lostmsu Nov 2, 2021
d3e4fba
assert check in GetUnmanagedBaseType for null base
lostmsu Nov 2, 2021
e003e12
PythonEngine .Exec and .Eval no longer work with raw pointers
lostmsu Nov 9, 2021
d0a6f44
a few annotation to ease debugging
lostmsu Nov 9, 2021
e31f7ba
ensure Python types continue to exist when registered decoders for th…
lostmsu Nov 9, 2021
48c0dfc
GC-related WIP
lostmsu Nov 9, 2021
cb58147
merge latest changes from upstream
lostmsu Nov 23, 2021
2fdbf0e
added TraceAlloc solution configuration
lostmsu Nov 24, 2021
a8ef06c
fixed sending PyObject across domain boundary
lostmsu Nov 24, 2021
7167229
fixed accidental premature disposal of Runtime.PyNone
lostmsu Nov 24, 2021
ab11fa2
made freeing GCHandles more robust
lostmsu Nov 24, 2021
7a4daeb
removed bad assert in generated constructor for derived classes
lostmsu Nov 24, 2021
e422367
fixed __pyobj__ access
lostmsu Nov 24, 2021
a74ea86
minor
lostmsu Nov 24, 2021
0325a8c
fixed Python derived types trying to double-free GCHandle when collec…
lostmsu Nov 24, 2021
85fab3b
reinstate collection assert on shutdown from Python
lostmsu Nov 24, 2021
932fce2
fixed crash when Python derived class instances survive past early sh…
lostmsu Nov 24, 2021
c2e207a
delay nulling GC handles of reflected instances until the last moment…
lostmsu Nov 24, 2021
c8f0f09
fixed assert in XDecref in case _Py_IsFinalizing is not present
lostmsu Nov 24, 2021
e269cf0
when initialized from Python, reset slots implemented in CLR: CLR mig…
lostmsu Nov 25, 2021
d7d5cb7
fixed minor warnings
lostmsu Nov 25, 2021
d6edace
fixed line endings in intern_.cs
lostmsu Nov 25, 2021
a86994f
use NonCopyableAnalyzer 0.7.0-m05
lostmsu Nov 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
GC-related WIP
  • Loading branch information
lostmsu committed Nov 9, 2021
commit 48c0dfc2102bb418a22c9ffbd2016dbfdb3ad551
15 changes: 15 additions & 0 deletions src/embed_tests/TestRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ public static void Py_IsInitializedValue()
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
}

[Test]
public static void IterAcrossRuns()
{
Runtime.Runtime.Py_Initialize();
BorrowedReference builtins = Runtime.Runtime.PyEval_GetBuiltins();
BorrowedReference iter = Runtime.Runtime.PyDict_GetItemString(builtins, "iter");

using var ownedIter = new NewReference(iter);
Runtime.Runtime.Py_Finalize();

Runtime.Runtime.Py_Initialize();
ownedIter.Dispose();
Runtime.Runtime.Py_Finalize();
}

[Test]
public static void RefCountTest()
{
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,58 +28,68 @@ internal static class Util
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int ReadInt32(BorrowedReference ob, int offset)
{
Debug.Assert(offset >= 0);
return Marshal.ReadInt32(ob.DangerousGetAddress(), offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long ReadInt64(BorrowedReference ob, int offset)
{
Debug.Assert(offset >= 0);
return Marshal.ReadInt64(ob.DangerousGetAddress(), offset);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe static T* ReadPtr<T>(BorrowedReference ob, int offset)
where T: unmanaged
{
Debug.Assert(offset >= 0);
IntPtr ptr = Marshal.ReadIntPtr(ob.DangerousGetAddress(), offset);
return (T*)ptr;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe static IntPtr ReadIntPtr(BorrowedReference ob, int offset)
{
Debug.Assert(offset >= 0);
return Marshal.ReadIntPtr(ob.DangerousGetAddress(), offset);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe static BorrowedReference ReadRef(BorrowedReference @ref, int offset)
{
Debug.Assert(offset >= 0);
return new BorrowedReference(ReadIntPtr(@ref, offset));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void WriteInt32(BorrowedReference ob, int offset, int value)
{
Debug.Assert(offset >= 0);
Marshal.WriteInt32(ob.DangerousGetAddress(), offset, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void WriteInt64(BorrowedReference ob, int offset, long value)
{
Debug.Assert(offset >= 0);
Marshal.WriteInt64(ob.DangerousGetAddress(), offset, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe static void WriteIntPtr(BorrowedReference ob, int offset, IntPtr value)
{
Debug.Assert(offset >= 0);
Marshal.WriteIntPtr(ob.DangerousGetAddress(), offset, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe static void WriteRef(BorrowedReference ob, int offset, in StolenReference @ref)
{
Debug.Assert(offset >= 0);
Marshal.WriteIntPtr(ob.DangerousGetAddress(), offset, @ref.DangerousGetAddress());
}

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

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/arrayobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
public new static NewReference mp_subscript(BorrowedReference ob, BorrowedReference idx)
{
var obj = (CLRObject)GetManagedObject(ob)!;
var arrObj = (ArrayObject)GetManagedObjectType(ob)!;
var arrObj = (ArrayObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!;
if (!arrObj.type.Valid)
{
return Exceptions.RaiseTypeError(arrObj.type.DeletedMessage);
Expand Down
22 changes: 13 additions & 9 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,33 +337,37 @@ public static NewReference tp_repr(BorrowedReference ob)
/// </summary>
public static void tp_dealloc(NewReference lastRef)
{
GCHandle? gcHandle = TryGetGCHandle(lastRef.Borrow());
Runtime.PyGC_ValidateLists();
Runtime.PyObject_GC_UnTrack(lastRef.Borrow());

tp_clear(lastRef.Borrow());
CallClear(lastRef.Borrow());

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

Runtime.PyObject_GC_UnTrack(lastRef.Borrow());
Runtime.PyObject_GC_Del(lastRef.Steal());

gcHandle?.Free();
DecrefTypeAndFree(lastRef.Steal());
Runtime.PyGC_ValidateLists();
}

public static int tp_clear(BorrowedReference ob)
{
Runtime.PyGC_ValidateLists();
GCHandle? gcHandle = TryGetGCHandle(ob);
gcHandle?.Free();

int baseClearResult = BaseUnmanagedClear(ob);
if (baseClearResult != 0)
{
return baseClearResult;
}

ClearObjectDict(ob);
Runtime.PyGC_ValidateLists();
return 0;
}

static unsafe int BaseUnmanagedClear(BorrowedReference ob)
internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
{
var type = Runtime.PyObject_TYPE(ob);
var unmanagedBase = GetUnmanagedBaseType(type);
Expand All @@ -374,10 +378,10 @@ static unsafe int BaseUnmanagedClear(BorrowedReference ob)
}
var clear = (delegate* unmanaged[Cdecl]<BorrowedReference, int>)clearPtr;

bool usesSubtypeClear = clearPtr == Util.ReadIntPtr(Runtime.CLRMetaType, TypeOffset.tp_clear);
bool usesSubtypeClear = clearPtr == TypeManager.subtype_clear;
if (usesSubtypeClear)
{
// workaround for https://bugs.python.org/issue45266
// workaround for https://bugs.python.org/issue45266 (subtype_clear)
using var dict = Runtime.PyObject_GenericGetDict(ob);
if (Runtime.PyMapping_HasKey(dict.Borrow(), PyIdentifier.__clear_reentry_guard__) != 0)
return 0;
Expand Down
54 changes: 5 additions & 49 deletions src/runtime/classderived.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Threading;

using Python.Runtime.Native;

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

public new static void tp_dealloc(NewReference ob)
{
Runtime.PyGC_ValidateLists();
var self = (CLRObject)GetManagedObject(ob.Borrow())!;

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

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

public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, object[] args)
{
Debug.Assert(Runtime.PyGILState_Check() != 0);

// call the base constructor
obj.GetType().InvokeMember(origCtorName,
BindingFlags.InvokeMethod,
Expand Down Expand Up @@ -833,58 +835,12 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
}
}

static readonly ConcurrentQueue<IntPtr> finalizeQueue = new();
static readonly Lazy<Thread> derivedFinalizer = new(() =>
{
var thread = new Thread(DerivedFinalizerMain)
{
IsBackground = true,
};
thread.Start();
return thread;
}, LazyThreadSafetyMode.ExecutionAndPublication);

static void DerivedFinalizerMain()
{
while (true)
{
if (0 == Runtime.Py_IsInitialized())
{
Thread.Sleep(millisecondsTimeout: 1000);
}

PyGILState gs = Runtime.PyGILState_Ensure();
try
{
while (finalizeQueue.Count > 0)
{
finalizeQueue.TryDequeue(out IntPtr obj);
var @ref = new BorrowedReference(obj);
GCHandle gcHandle = ManagedType.GetGCHandle(@ref);

bool deleted = CLRObject.reflectedObjects.Remove(obj);
Debug.Assert(deleted);
Runtime.PyObject_GC_Del(@ref);

gcHandle.Free();
}

}
finally
{
Runtime.PyGILState_Release(gs);
}
}
}
public static void PyFinalize(IPythonDerivedType obj)
{
// the C# object is being destroyed which must mean there are no more
// references to the Python object as well
var self = GetPyObj(obj).DangerousGetAddress();
finalizeQueue.Enqueue(self);
SetPyObj(obj, null);

GC.KeepAlive(derivedFinalizer.Value);
Finalizer.Instance.AddDerivedFinalizedObject(ref self);
}

internal static BorrowedReference GetPyObj(IPythonDerivedType obj)
Expand Down
17 changes: 0 additions & 17 deletions src/runtime/classmanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,6 @@ internal static void RemoveClasses()
cache.Clear();
}

private static int TraverseTypeClear(BorrowedReference ob, IntPtr arg)
{
var visited = (HashSet<IntPtr>)GCHandle.FromIntPtr(arg).Target;
if (!visited.Add(ob.DangerousGetAddressOrNull()))
{
return 0;
}
var clrObj = ManagedType.GetManagedObject(ob);
if (clrObj != null)
{
BorrowedReference tp = Runtime.PyObject_TYPE(ob);
ManagedType.CallTypeTraverse(ob, tp, TraverseTypeClear, arg);
ManagedType.CallTypeClear(ob, tp);
}
return 0;
}

internal static ClassManagerState SaveRuntimeData()
{
var contexts = new Dictionary<ReflectedClrType, InterDomainContext>();
Expand Down
42 changes: 12 additions & 30 deletions src/runtime/constructorbinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,24 +147,15 @@ public static NewReference tp_repr(BorrowedReference ob)
return new NewReference(self.repr);
}

protected override void Clear(BorrowedReference ob)
{
Runtime.Py_CLEAR(ref this.repr);
base.Clear(ob);
}

public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg)
{
var self = (ConstructorBinding)GetManagedObject(ob)!;
int res = PyVisit(self.typeToCreate, visit, arg);
if (res != 0) return res;
var self = (ConstructorBinding?)GetManagedObject(ob);
if (self is null) return 0;

if (self.repr is not null)
{
res = PyVisit(self.repr, visit, arg);
if (res != 0) return res;
}
return 0;
Runtime.PyGC_ValidateLists();
int res = PyVisit(self.typeToCreate, visit, arg);
Runtime.PyGC_ValidateLists();
return res;
}
}

Expand Down Expand Up @@ -241,24 +232,15 @@ public static NewReference tp_repr(BorrowedReference ob)
return new NewReference(self.repr);
}

protected override void Clear(BorrowedReference ob)
{
Runtime.Py_CLEAR(ref this.repr);
base.Clear(ob);
}

public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg)
{
var self = (BoundContructor)GetManagedObject(ob)!;
int res = PyVisit(self.typeToCreate, visit, arg);
if (res != 0) return res;
var self = (BoundContructor?)GetManagedObject(ob);
if (self is null) return 0;

if (self.repr is not null)
{
res = PyVisit(self.repr, visit, arg);
if (res != 0) return res;
}
return 0;
Runtime.PyGC_ValidateLists();
int res = PyVisit(self.typeToCreate, visit, arg);
Runtime.PyGC_ValidateLists();
return res;
}
}
}
Loading