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; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy