diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 1beddbec9..157e60803 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -421,13 +421,18 @@ public PyObject TryEncode(object value) } } - class InstancelessExceptionDecoder : IPyObjectDecoder + class InstancelessExceptionDecoder : IPyObjectDecoder, IDisposable { readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr"); public bool CanDecode(PyType objectType, Type targetType) => PythonReferenceComparer.Instance.Equals(PyErr, objectType); + public void Dispose() + { + PyErr.Dispose(); + } + public bool TryDecode(PyObject pyObj, out T value) { if (pyObj.HasAttr("value")) diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs index 9a832cb0c..dff58b978 100644 --- a/src/embed_tests/GlobalTestsSetup.cs +++ b/src/embed_tests/GlobalTestsSetup.cs @@ -9,6 +9,22 @@ namespace Python.EmbeddingTest [SetUpFixture] public partial class GlobalTestsSetup { + [OneTimeSetUp] + public void GlobalSetup() + { + Finalizer.Instance.ErrorHandler += FinalizerErrorHandler; + } + + private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e) + { + if (e.Error is RuntimeShutdownException) + { + // allow objects to leak after the python runtime run + // they were created in is gone + e.Handled = true; + } + } + [OneTimeTearDown] public void FinalCleanup() { diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs index 58d66ed96..7de5277a1 100644 --- a/src/embed_tests/Inheritance.cs +++ b/src/embed_tests/Inheritance.cs @@ -24,6 +24,7 @@ public void SetUp() [OneTimeTearDown] public void Dispose() { + ExtraBaseTypeProvider.ExtraBase.Dispose(); PythonEngine.Shutdown(); } diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs index e4479da18..e7b330d12 100644 --- a/src/embed_tests/TestDomainReload.cs +++ b/src/embed_tests/TestDomainReload.cs @@ -179,116 +179,6 @@ public static void CrossDomainObject() #endregion - #region Tempary tests - - // https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665 - [Test] - public void CrossReleaseBuiltinType() - { - void ExecTest() - { - try - { - PythonEngine.Initialize(); - var numRef = CreateNumReference(); - Assert.True(numRef.IsAlive); - PythonEngine.Shutdown(); // <- "run" 1 ends - PythonEngine.Initialize(); // <- "run" 2 starts - - GC.Collect(); - GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue - Finalizer.Instance.Collect(); - // ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`, - // but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead. - Assert.False(numRef.IsAlive); - } - finally - { - PythonEngine.Shutdown(); - } - } - - var errorArgs = new List(); - void ErrorHandler(object sender, Finalizer.ErrorArgs e) - { - errorArgs.Add(e); - } - Finalizer.Instance.ErrorHandler += ErrorHandler; - try - { - for (int i = 0; i < 10; i++) - { - ExecTest(); - } - } - finally - { - Finalizer.Instance.ErrorHandler -= ErrorHandler; - } - Assert.AreEqual(errorArgs.Count, 0); - } - - [Test] - public void CrossReleaseCustomType() - { - void ExecTest() - { - try - { - PythonEngine.Initialize(); - var objRef = CreateConcreateObject(); - Assert.True(objRef.IsAlive); - PythonEngine.Shutdown(); // <- "run" 1 ends - PythonEngine.Initialize(); // <- "run" 2 starts - GC.Collect(); - GC.WaitForPendingFinalizers(); - Finalizer.Instance.Collect(); - Assert.False(objRef.IsAlive); - } - finally - { - PythonEngine.Shutdown(); - } - } - - var errorArgs = new List(); - void ErrorHandler(object sender, Finalizer.ErrorArgs e) - { - errorArgs.Add(e); - } - Finalizer.Instance.ErrorHandler += ErrorHandler; - try - { - for (int i = 0; i < 10; i++) - { - ExecTest(); - } - } - finally - { - Finalizer.Instance.ErrorHandler -= ErrorHandler; - } - Assert.AreEqual(errorArgs.Count, 0); - } - - private static WeakReference CreateNumReference() - { - var num = 3216757418.ToPython(); - Assert.AreEqual(num.Refcount, 1); - WeakReference numRef = new WeakReference(num, false); - return numRef; - } - - private static WeakReference CreateConcreateObject() - { - var obj = new Domain.MyClass().ToPython(); - Assert.AreEqual(obj.Refcount, 1); - WeakReference numRef = new WeakReference(obj, false); - return numRef; - } - - #endregion Tempary tests - /// /// This is a magic incantation required to run code in an application /// domain other than the current one. diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index a15aff585..61ed9c6fb 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -40,8 +40,10 @@ public static void LoadSpecificArgs() { using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) { - Assert.AreEqual(args[0], argv[0].ToString()); - Assert.AreEqual(args[1], argv[1].ToString()); + using var v0 = argv[0]; + using var v1 = argv[1]; + Assert.AreEqual(args[0], v0.ToString()); + Assert.AreEqual(args[1], v1.ToString()); } } } @@ -54,12 +56,16 @@ public void ImportClassShutdownRefcount() PyObject ns = Py.Import(typeof(ImportClassShutdownRefcountClass).Namespace); PyObject cls = ns.GetAttr(nameof(ImportClassShutdownRefcountClass)); + BorrowedReference clsRef = cls.Reference; +#pragma warning disable CS0618 // Type or member is obsolete + cls.Leak(); +#pragma warning restore CS0618 // Type or member is obsolete ns.Dispose(); - Assert.Less(cls.Refcount, 256); + Assert.Less(Runtime.Runtime.Refcount(clsRef), 256); PythonEngine.Shutdown(); - Assert.Greater(cls.Refcount, 0); + Assert.Greater(Runtime.Runtime.Refcount(clsRef), 0); } /// @@ -176,6 +182,7 @@ public static void TestRunExitFuncs() { Assert.Fail(msg); } + PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault(); return; } bool called = false; @@ -187,6 +194,7 @@ public static void TestRunExitFuncs() atexit.Dispose(); Runtime.Runtime.Shutdown(); Assert.True(called); + PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault(); } } diff --git a/src/runtime/Codecs/DecoderGroup.cs b/src/runtime/Codecs/DecoderGroup.cs index b72cd796c..cf0ee33e9 100644 --- a/src/runtime/Codecs/DecoderGroup.cs +++ b/src/runtime/Codecs/DecoderGroup.cs @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs /// /// Represents a group of s. Useful to group them by priority. /// - public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable + public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable, IDisposable { readonly List decoders = new List(); @@ -46,6 +46,15 @@ public bool TryDecode(PyObject pyObj, out T value) /// public IEnumerator GetEnumerator() => this.decoders.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator(); + + public void Dispose() + { + foreach (var decoder in this.decoders.OfType()) + { + decoder.Dispose(); + } + this.decoders.Clear(); + } } public static class DecoderGroupExtensions diff --git a/src/runtime/Codecs/EncoderGroup.cs b/src/runtime/Codecs/EncoderGroup.cs index 4f776a669..6c40623ca 100644 --- a/src/runtime/Codecs/EncoderGroup.cs +++ b/src/runtime/Codecs/EncoderGroup.cs @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs /// /// Represents a group of s. Useful to group them by priority. /// - public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable + public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable, IDisposable { readonly List encoders = new List(); @@ -47,6 +47,15 @@ public PyObject TryEncode(object value) /// public IEnumerator GetEnumerator() => this.encoders.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator(); + + public void Dispose() + { + foreach (var encoder in this.encoders.OfType()) + { + encoder.Dispose(); + } + this.encoders.Clear(); + } } public static class EncoderGroupExtensions diff --git a/src/runtime/InteropConfiguration.cs b/src/runtime/InteropConfiguration.cs index 78af5037a..30c9a1c2c 100644 --- a/src/runtime/InteropConfiguration.cs +++ b/src/runtime/InteropConfiguration.cs @@ -2,10 +2,11 @@ namespace Python.Runtime { using System; using System.Collections.Generic; + using System.Linq; using Python.Runtime.Mixins; - public sealed class InteropConfiguration + public sealed class InteropConfiguration: IDisposable { internal readonly PythonBaseTypeProviderGroup pythonBaseTypeProviders = new PythonBaseTypeProviderGroup(); @@ -24,5 +25,14 @@ public static InteropConfiguration MakeDefault() }, }; } + + public void Dispose() + { + foreach (var provider in PythonBaseTypeProviders.OfType()) + { + provider.Dispose(); + } + PythonBaseTypeProviders.Clear(); + } } } diff --git a/src/runtime/Mixins/CollectionMixinsProvider.cs b/src/runtime/Mixins/CollectionMixinsProvider.cs index 48ea35f1c..e01a6be0d 100644 --- a/src/runtime/Mixins/CollectionMixinsProvider.cs +++ b/src/runtime/Mixins/CollectionMixinsProvider.cs @@ -4,7 +4,7 @@ namespace Python.Runtime.Mixins { - class CollectionMixinsProvider : IPythonBaseTypeProvider + class CollectionMixinsProvider : IPythonBaseTypeProvider, IDisposable { readonly Lazy mixinsModule; public CollectionMixinsProvider(Lazy mixinsModule) @@ -86,5 +86,13 @@ static Type[] NewInterfaces(Type type) static Type GetDefinition(Type type) => type.IsGenericType ? type.GetGenericTypeDefinition() : type; + + public void Dispose() + { + if (this.mixinsModule.IsValueCreated) + { + this.mixinsModule.Value.Dispose(); + } + } } } diff --git a/src/runtime/converterextensions.cs b/src/runtime/converterextensions.cs index 2396fb0bd..dfc2ecc21 100644 --- a/src/runtime/converterextensions.cs +++ b/src/runtime/converterextensions.cs @@ -166,8 +166,8 @@ internal static void Reset() { clrToPython.Clear(); pythonToClr.Clear(); - encoders.Clear(); - decoders.Clear(); + encoders.Dispose(); + decoders.Dispose(); } } diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs index 5153c13ad..0acf5254d 100644 --- a/src/runtime/finalizer.cs +++ b/src/runtime/finalizer.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -30,10 +31,12 @@ public class ErrorArgs : EventArgs [DefaultValue(DefaultThreshold)] public int Threshold { get; set; } = DefaultThreshold; + bool started; + [DefaultValue(true)] public bool Enable { get; set; } = true; - private ConcurrentQueue _objQueue = new ConcurrentQueue(); + private ConcurrentQueue _objQueue = new (); private int _throttled; #region FINALIZER_CHECK @@ -79,6 +82,8 @@ internal IncorrectRefCountException(IntPtr ptr) internal void ThrottledCollect() { + if (!started) throw new InvalidOperationException($"{nameof(PythonEngine)} is not initialized"); + _throttled = unchecked(this._throttled + 1); if (!Enable || _throttled < Threshold) return; _throttled = 0; @@ -87,12 +92,13 @@ internal void ThrottledCollect() internal List GetCollectedObjects() { - return _objQueue.ToList(); + return _objQueue.Select(o => o.PyObj).ToList(); } - internal void AddFinalizedObject(ref IntPtr obj) + internal void AddFinalizedObject(ref IntPtr obj, int run) { - if (!Enable || obj == IntPtr.Zero) + Debug.Assert(obj != IntPtr.Zero); + if (!Enable) { return; } @@ -101,14 +107,20 @@ internal void AddFinalizedObject(ref IntPtr obj) lock (_queueLock) #endif { - this._objQueue.Enqueue(obj); + this._objQueue.Enqueue(new PendingFinalization { PyObj = obj, RuntimeRun = run }); } obj = IntPtr.Zero; } + internal static void Initialize() + { + Instance.started = true; + } + internal static void Shutdown() { Instance.DisposeAll(); + Instance.started = false; } private void DisposeAll() @@ -124,36 +136,31 @@ private void DisposeAll() #if FINALIZER_CHECK ValidateRefCount(); #endif - IntPtr obj; Runtime.PyErr_Fetch(out var errType, out var errVal, out var traceback); + int run = Runtime.GetRun(); + try { while (!_objQueue.IsEmpty) { - if (!_objQueue.TryDequeue(out obj)) + if (!_objQueue.TryDequeue(out var obj)) + continue; + + if (obj.RuntimeRun != run) + { + HandleFinalizationException(obj.PyObj, new RuntimeShutdownException(obj.PyObj)); continue; + } - Runtime.XDecref(obj); + Runtime.XDecref(obj.PyObj); try { Runtime.CheckExceptionOccurred(); } catch (Exception e) { - var errorArgs = new ErrorArgs - { - Error = e, - }; - - ErrorHandler?.Invoke(this, errorArgs); - - if (!errorArgs.Handled) - { - throw new FinalizationException( - "Python object finalization failed", - disposable: obj, innerException: e); - } + HandleFinalizationException(obj.PyObj, e); } } } @@ -166,6 +173,23 @@ private void DisposeAll() } } + void HandleFinalizationException(IntPtr obj, Exception cause) + { + var errorArgs = new ErrorArgs + { + Error = cause, + }; + + ErrorHandler?.Invoke(this, errorArgs); + + if (!errorArgs.Handled) + { + throw new FinalizationException( + "Python object finalization failed", + disposable: obj, innerException: cause); + } + } + #if FINALIZER_CHECK private void ValidateRefCount() { @@ -235,6 +259,12 @@ private void ValidateRefCount() #endif } + struct PendingFinalization + { + public IntPtr PyObj; + public int RuntimeRun; + } + public class FinalizationException : Exception { public IntPtr Handle { get; } @@ -259,5 +289,21 @@ public FinalizationException(string message, IntPtr disposable, Exception innerE if (disposable == IntPtr.Zero) throw new ArgumentNullException(nameof(disposable)); this.Handle = disposable; } + + protected FinalizationException(string message, IntPtr disposable) + : base(message) + { + if (disposable == IntPtr.Zero) throw new ArgumentNullException(nameof(disposable)); + this.Handle = disposable; + } + } + + public class RuntimeShutdownException : FinalizationException + { + public RuntimeShutdownException(IntPtr disposable) + : base("Python runtime was shut down after this object was created." + + " It is an error to attempt to dispose or to continue using it even after restarting the runtime.", disposable) + { + } } } diff --git a/src/runtime/pybuffer.cs b/src/runtime/pybuffer.cs index 7161a864f..94741b19b 100644 --- a/src/runtime/pybuffer.cs +++ b/src/runtime/pybuffer.cs @@ -236,11 +236,12 @@ private void Dispose(bool disposing) ~PyBuffer() { - if (disposedValue) + Debug.Assert(!disposedValue); + + if (_view.obj != IntPtr.Zero) { - return; + Finalizer.Instance.AddFinalizedObject(ref _view.obj, _exporter.run); } - Finalizer.Instance.AddFinalizedObject(ref _view.obj); } /// diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index bd767307b..747b4ecdf 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -1,10 +1,10 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Linq.Expressions; +using System.Threading; namespace Python.Runtime { @@ -27,6 +27,7 @@ public partial class PyObject : DynamicObject, IDisposable #endif protected internal IntPtr obj = IntPtr.Zero; + internal readonly int run = Runtime.GetRun(); public static PyObject None => new PyObject(new BorrowedReference(Runtime.PyNone)); internal BorrowedReference Reference => new BorrowedReference(this.obj); @@ -95,11 +96,19 @@ internal PyObject(in StolenReference reference) // when the managed wrapper is garbage-collected. ~PyObject() { - if (obj == IntPtr.Zero) + if (obj != IntPtr.Zero) { - return; + +#if TRACE_ALLOC + CheckRun(); +#endif + + Interlocked.Increment(ref Runtime._collected); + + Finalizer.Instance.AddFinalizedObject(ref obj, run); } - Finalizer.Instance.AddFinalizedObject(ref obj); + + Dispose(false); } @@ -167,17 +176,6 @@ public object AsManagedObject(Type t) internal bool IsDisposed => obj == IntPtr.Zero; - /// - /// Dispose Method - /// - /// - /// The Dispose method provides a way to explicitly release the - /// Python object represented by a PyObject instance. It is a good - /// idea to call Dispose on PyObjects that wrap resources that are - /// limited or need strict lifetime control. Otherwise, references - /// to Python objects will not be released until a managed garbage - /// collection occurs. - /// protected virtual void Dispose(bool disposing) { if (this.obj == IntPtr.Zero) @@ -188,6 +186,8 @@ protected virtual void Dispose(bool disposing) if (Runtime.Py_IsInitialized() == 0) throw new InvalidOperationException("Python runtime must be initialized"); + CheckRun(); + if (!Runtime.IsFinalizing) { long refcount = Runtime.Refcount(this.obj); @@ -221,10 +221,32 @@ protected virtual void Dispose(bool disposing) this.obj = IntPtr.Zero; } + /// + /// The Dispose method provides a way to explicitly release the + /// Python object represented by a PyObject instance. It is a good + /// idea to call Dispose on PyObjects that wrap resources that are + /// limited or need strict lifetime control. Otherwise, references + /// to Python objects will not be released until a managed garbage + /// collection occurs. + /// public void Dispose() { + GC.SuppressFinalize(this); Dispose(true); + } + + [Obsolete("Test use only")] + internal void Leak() + { + Debug.Assert(obj != IntPtr.Zero); GC.SuppressFinalize(this); + obj = IntPtr.Zero; + } + + internal void CheckRun() + { + if (run != Runtime.GetRun()) + throw new RuntimeShutdownException(obj); } internal BorrowedReference GetPythonTypeReference() diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 91e013e86..6e0057036 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -317,6 +317,8 @@ public static IntPtr InitExt() { Initialize(setSysArgv: false, mode: ShutdownMode.Extension); + Finalizer.Instance.ErrorHandler += AllowLeaksDuringShutdown; + // Trickery - when the import hook is installed into an already // running Python, the standard import machinery is still in // control for the duration of the import that caused bootstrap. @@ -358,6 +360,14 @@ public static IntPtr InitExt() .DangerousMoveToPointerOrNull(); } + private static void AllowLeaksDuringShutdown(object sender, Finalizer.ErrorArgs e) + { + if (e.Error is RuntimeShutdownException) + { + e.Handled = true; + } + } + /// /// Shutdown Method /// @@ -381,7 +391,6 @@ public static void Shutdown(ShutdownMode mode) ExecuteShutdownHandlers(); // Remember to shut down the runtime. Runtime.Shutdown(mode); - PyObjectConversions.Reset(); initialized = false; diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index b4b045b4a..217075494 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -85,7 +85,16 @@ internal static Version PyVersion } } - /// + const string RunSysPropName = "__pythonnet_run__"; + static int run = 0; + + internal static int GetRun() + { + int runNumber = run; + Debug.Assert(runNumber > 0, "This must only be called after Runtime is initialized at least once"); + return runNumber; + } + /// Initialize the runtime... /// /// Always call this method from the Main thread. After the @@ -110,6 +119,9 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd if (!interpreterAlreadyInitialized) { Py_InitializeEx(initSigs ? 1 : 0); + + NewRun(); + if (PyEval_ThreadsInitialized() == 0) { PyEval_InitThreads(); @@ -130,10 +142,21 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd { PyGILState_Ensure(); } + + BorrowedReference pyRun = PySys_GetObject(RunSysPropName); + if (pyRun != null) + { + run = checked((int)PyLong_AsSignedSize_t(pyRun)); + } + else + { + NewRun(); + } } MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; IsFinalizing = false; + Finalizer.Initialize(); InternString.Initialize(); InitPyMembers(); @@ -175,6 +198,13 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd inspect = GetModuleLazy("inspect"); } + static void NewRun() + { + run++; + using var pyRun = PyLong_FromLongLong(run); + PySys_SetObject(RunSysPropName, pyRun); + } + private static void InitPyMembers() { IntPtr op; @@ -345,26 +375,29 @@ internal static void Shutdown(ShutdownMode mode) PyCLRMetaType = IntPtr.Zero; Exceptions.Shutdown(); + PythonEngine.InteropConfiguration.Dispose(); + DisposeLazyModule(clrInterop); + DisposeLazyModule(inspect); + PyObjectConversions.Reset(); + + if (mode != ShutdownMode.Extension) + { + PyGC_Collect(); + bool everythingSeemsCollected = TryCollectingGarbage(); + Debug.Assert(everythingSeemsCollected); + } + Finalizer.Shutdown(); InternString.Shutdown(); if (mode != ShutdownMode.Normal && mode != ShutdownMode.Extension) { - PyGC_Collect(); if (mode == ShutdownMode.Soft) { RuntimeState.Restore(); } ResetPyMembers(); GC.Collect(); - try - { - GC.WaitForFullGCComplete(); - } - catch (NotImplementedException) - { - // Some clr runtime didn't implement GC.WaitForFullGCComplete yet. - } GC.WaitForPendingFinalizers(); PyGILState_Release(state); // Then release the GIL for good, if there is somehting to release @@ -390,12 +423,40 @@ internal static void Shutdown(ShutdownMode mode) } } + const int MaxCollectRetriesOnShutdown = 20; + internal static int _collected; + static bool TryCollectingGarbage() + { + for (int attempt = 0; attempt < MaxCollectRetriesOnShutdown; attempt++) + { + Interlocked.Exchange(ref _collected, 0); + nint pyCollected = 0; + for (int i = 0; i < 2; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + pyCollected += PyGC_Collect(); + } + if (Volatile.Read(ref _collected) == 0 && pyCollected == 0) + return true; + } + return false; + } + internal static void Shutdown() { var mode = ShutdownMode; Shutdown(mode); } + static void DisposeLazyModule(Lazy module) + { + if (module.IsValueCreated) + { + module.Value.Dispose(); + } + } + private static Lazy GetModuleLazy(string moduleName) => moduleName is null ? throw new ArgumentNullException(nameof(moduleName)) @@ -777,6 +838,10 @@ internal static unsafe long Refcount(IntPtr op) return *p; } + [Pure] + internal static long Refcount(BorrowedReference op) + => Refcount(op.DangerousGetAddress()); + /// /// Call specified function, and handle PythonDLL-related failures. /// @@ -2171,7 +2236,7 @@ internal static int PyException_SetTraceback(BorrowedReference ex, BorrowedRefer - internal static IntPtr PyGC_Collect() => Delegates.PyGC_Collect(); + internal static nint PyGC_Collect() => Delegates.PyGC_Collect(); internal static IntPtr _Py_AS_GC(BorrowedReference ob) { @@ -2551,7 +2616,7 @@ static Delegates() PyErr_Print = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Print), GetUnmanagedDll(_PythonDll)); PyCell_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Get), GetUnmanagedDll(_PythonDll)); PyCell_Set = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Set), GetUnmanagedDll(_PythonDll)); - PyGC_Collect = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGC_Collect), GetUnmanagedDll(_PythonDll)); + PyGC_Collect = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGC_Collect), GetUnmanagedDll(_PythonDll)); PyCapsule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_New), GetUnmanagedDll(_PythonDll)); PyCapsule_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_GetPointer), GetUnmanagedDll(_PythonDll)); PyCapsule_SetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_SetPointer), GetUnmanagedDll(_PythonDll)); @@ -2830,7 +2895,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyErr_Print { get; } internal static delegate* unmanaged[Cdecl] PyCell_Get { get; } internal static delegate* unmanaged[Cdecl] PyCell_Set { get; } - internal static delegate* unmanaged[Cdecl] PyGC_Collect { get; } + internal static delegate* unmanaged[Cdecl] PyGC_Collect { get; } internal static delegate* unmanaged[Cdecl] PyCapsule_New { get; } internal static delegate* unmanaged[Cdecl] PyCapsule_GetPointer { get; } internal static delegate* unmanaged[Cdecl] PyCapsule_SetPointer { get; }