[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pythonnet/pythonnet/just/src/runtime/Native/ABI.cs [Back]  [Original]

using static System.FormattableString;
using System;
using System.Linq;
using System.Reflection;


namespace Python.Runtime.Native
{
    static class ABI
    {
        // GIL builds only. FT splits the refcount; Refcount uses Py_REFCNT.
        public static int RefCountOffset { get; private set; }

        // Added to generated TypeOffsets. The FT header is 12 bytes larger on
        // 32-bit and 16 bytes larger on 64-bit builds.
        public static int ObjectHeadOffset { get; private set; }

        public static bool IsFreeThreaded { get; private set; }

        internal static void Initialize(Version version)
        {
            IsFreeThreaded = DetectFreeThreaded();
            if (IsFreeThreaded && version < new Version(3, 14))
            {
                // We need Py_REFCNT to be available as a symbol, which is only
                // present in Python 3.14 and later.
                throw new NotSupportedException($"Python ABI v{version} is not supported: Free-threaded builds are only supported on Python 3.14 and later.");
            }

            RefCountOffset = IsFreeThreaded ? -1 : ProbeRefCountOffset();
            ObjectHeadOffset = IsFreeThreaded
                ? (IntPtr.Size == 4 ? 12 : 16)
                : RefCountOffset;

            var thisAssembly = Assembly.GetExecutingAssembly();

            const string nativeTypeOffsetClassName = "Python.Runtime.NativeTypeOffset";
            string className = Invariant($"Python.Runtime.TypeOffset{version.Major}{version.Minor}");

            Type nativeOffsetsClass = thisAssembly.GetType(nativeTypeOffsetClassName, throwOnError: false);
            Type typeOffsetsClass =
                // Try platform native offsets first. It is only present when generated by setup.py
                nativeOffsetsClass ?? thisAssembly.GetType(className, throwOnError: false);

            if (typeOffsetsClass is null)
            {
                var types = thisAssembly.GetTypes().Select(type => type.Name).Where(name => name.StartsWith("TypeOffset"));
                string message = $"Searching for {className}, found {string.Join(",", types)}.";
                throw new NotSupportedException($"Python ABI v{version} is not supported: {message}");
            }

            var typeOffsets = (ITypeOffsets)Activator.CreateInstance(typeOffsetsClass);
            TypeOffset.Use(typeOffsets, nativeOffsetsClass == null ? ObjectHeadOffset : 0);
        }

        /// 
        /// True when the running interpreter is a Py_GIL_DISABLED (free-threaded) build.
        /// 
        static bool DetectFreeThreaded()
        {
            // sys._is_gil_enabled() was added in Python 3.13; absent means GIL build.
            using var sys = Runtime.PyImport_ImportModule("sys");

            if (sys.IsNull())
            {
                Runtime.PyErr_Clear();
                return false;
            }

            using var func = Runtime.PyObject_GetAttrString(sys.Borrow(), "_is_gil_enabled");

            if (func.IsNull())
            {
                Runtime.PyErr_Clear();
                return false;
            }

            using var args = Runtime.PyTuple_New(0);
            using var result = Runtime.PyObject_Call(func.Borrow(), args.Borrow(), default);

            if (result.IsNull())
            {
                Runtime.PyErr_Clear();
                return false;
            }

            return Runtime.PyObject_IsTrue(result.Borrow()) == 0;
        }

        /// 
        /// Locates the offset of ob_refcnt inside PyObject by allocating a
        /// fresh list and scanning for its refcount value of 1.  GIL builds only.
        /// 
        static unsafe int ProbeRefCountOffset()
        {
            using var tempObject = Runtime.PyList_New(0);
            IntPtr* tempPtr = (IntPtr*)tempObject.DangerousGetAddress();
            int offset = 0;
            while (tempPtr[offset] != (IntPtr)1)
            {
                offset++;
                if (offset > 100)
                    throw new InvalidProgramException("PyObject_HEAD could not be found withing reasonable distance from the start of PyObject");
            }
            return offset * IntPtr.Size;
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page