[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pythonnet/pythonnet/v3.2.0/src/runtime/Types/ExtensionType.cs [Back]  [Original]

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Python.Runtime
{
    /// 
    /// Base class for extensions whose instances *share* a single Python
    /// type object, such as the types that represent CLR methods, fields,
    /// etc. Instances implemented by this class do not support sub-typing.
    /// 
    [Serializable]
    internal abstract class ExtensionType : ManagedType
    {
        public virtual NewReference Alloc()
        {
            // Create a new PyObject whose type is a generated type that is
            // implemented by the particular concrete ExtensionType subclass.
            // The Python instance object is related to an instance of a
            // particular concrete subclass with a hidden CLR gchandle.

            BorrowedReference tp = TypeManager.GetTypeReference(GetType());

            //int rc = (int)Util.ReadIntPtr(tp, TypeOffset.ob_refcnt);
            //if (rc > 1050)
            //{
            //    DebugUtil.Print("tp is: ", tp);
            //    DebugUtil.DumpType(tp);
            //}

            NewReference py = Runtime.PyType_GenericAlloc(tp, 0);

#if DEBUG
            GetGCHandle(py.BorrowOrThrow(), tp, out var existing);
            System.Diagnostics.Debug.Assert(existing == IntPtr.Zero);
#endif
            SetupGc(py.Borrow(), tp);

            return py;
        }

        public PyObject AllocObject() => new(Alloc().Steal());

        internal static readonly ConcurrentDictionary loadedExtensions = new();
        void SetupGc (BorrowedReference ob, BorrowedReference tp)
        {
            GCHandle gc = GCHandle.Alloc(this);
            InitGCHandle(ob, tp, gc);

            bool isNew = loadedExtensions.TryAdd(ob.DangerousGetAddress(), 0);
            Debug.Assert(isNew);

            // We have to support gc because the type machinery makes it very
            // hard not to - but we really don't have a need for it in most
            // concrete extension types, so untrack the object to save calls
            // from Python into the managed runtime that are pure overhead.

            Runtime.PyObject_GC_UnTrack(ob);
        }

        /// 
        /// Type __setattr__ implementation.
        /// 
        public static int tp_setattro(BorrowedReference ob, BorrowedReference key, BorrowedReference val)
        {
            var message = "type does not support setting attributes";
            if (val == null)
            {
                message = "readonly attribute";
            }
            Exceptions.SetError(Exceptions.AttributeError, message);
            return -1;
        }

        public unsafe static void tp_dealloc(NewReference lastRef)
        {
            Runtime.PyObject_GC_UnTrack(lastRef.Borrow());

            tp_clear(lastRef.Borrow());

            // we must decref our type: https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_dealloc
            DecrefTypeAndFree(lastRef.Steal());
        }

        /// 
        /// Called during tp_clear before the GCHandle is released.
        /// Override to eagerly dispose Python object references (PyObject fields)
        /// held by the subclass, preventing the multi-hop .NET finalizer chain
        /// from delaying Python-side refcount decrements.
        /// 
        protected virtual void OnClear() { }

        public static int tp_clear(BorrowedReference ob)
        {
            (GetManagedObject(ob) as ExtensionType)?.OnClear();

            var weakrefs = Runtime.PyObject_GetWeakRefList(ob);
            if (weakrefs != null)
            {
                Runtime.PyObject_ClearWeakRefs(ob);
            }

            if (TryFreeGCHandle(ob))
            {
                bool deleted = loadedExtensions.TryRemove(ob.DangerousGetAddress(), out _);
                Debug.Assert(deleted);
            }

            int res = ClassBase.BaseUnmanagedClear(ob);
            return res;
        }

        protected override void OnLoad(BorrowedReference ob, Dictionary? context)
        {
            base.OnLoad(ob, context);
            SetupGc(ob, Runtime.PyObject_TYPE(ob));
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page