gh-146636: abi3t: Define Py_GIL_DISABLED but do not use it (GH-148142)

When compiling for abi3t, define Py_GIL_DISABLED, so that users who
check it to enable additional locking aren't broken.

But also avoid using Py_GIL_DISABLED in Python headers themselves
-- abi3 and abi3t ought to be the same except
the _Py_OPAQUE_PYOBJECT differences.

A check for this is coming in a later PR.
It will require rewriting some preprocessor conditions, some of these
changes are included in this PR.
For _Py_IsOwnedByCurrentThread & supporting functions
I opted to move them to a cpython/ header, as they're rather self-contained.
This commit is contained in:
Petr Viktorin 2026-04-07 09:06:17 +02:00 committed by GitHub
parent 5e9d90b615
commit fbc1a5b076
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 115 additions and 96 deletions

View file

@ -495,3 +495,82 @@ PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *);
PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *);
PyAPI_FUNC(int) PyUnstable_SetImmortal(PyObject *op);
#if defined(Py_GIL_DISABLED)
PyAPI_FUNC(uintptr_t) _Py_GetThreadLocal_Addr(void);
static inline uintptr_t
_Py_ThreadId(void)
{
uintptr_t tid;
#if defined(_MSC_VER) && defined(_M_X64)
tid = __readgsqword(48);
#elif defined(_MSC_VER) && defined(_M_IX86)
tid = __readfsdword(24);
#elif defined(_MSC_VER) && defined(_M_ARM64)
tid = __getReg(18);
#elif defined(__MINGW32__) && defined(_M_X64)
tid = __readgsqword(48);
#elif defined(__MINGW32__) && defined(_M_IX86)
tid = __readfsdword(24);
#elif defined(__MINGW32__) && defined(_M_ARM64)
tid = __getReg(18);
#elif defined(__i386__)
__asm__("{movl %%gs:0, %0|mov %0, dword ptr gs:[0]}" : "=r" (tid)); // 32-bit always uses GS
#elif defined(__MACH__) && defined(__x86_64__)
__asm__("{movq %%gs:0, %0|mov %0, qword ptr gs:[0]}" : "=r" (tid)); // x86_64 macOSX uses GS
#elif defined(__x86_64__)
__asm__("{movq %%fs:0, %0|mov %0, qword ptr fs:[0]}" : "=r" (tid)); // x86_64 Linux, BSD uses FS
#elif defined(__arm__) && __ARM_ARCH >= 7
__asm__ ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tid));
#elif defined(__aarch64__) && defined(__APPLE__)
__asm__ ("mrs %0, tpidrro_el0" : "=r" (tid));
#elif defined(__aarch64__)
__asm__ ("mrs %0, tpidr_el0" : "=r" (tid));
#elif defined(__powerpc64__)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// r13 is reserved for use as system thread ID by the Power 64-bit ABI.
register uintptr_t tp __asm__ ("r13");
__asm__("" : "=r" (tp));
tid = tp;
#endif
#elif defined(__powerpc__)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// r2 is reserved for use as system thread ID by the Power 32-bit ABI.
register uintptr_t tp __asm__ ("r2");
__asm__ ("" : "=r" (tp));
tid = tp;
#endif
#elif defined(__s390__) && defined(__GNUC__)
// Both GCC and Clang have supported __builtin_thread_pointer
// for s390 from long time ago.
tid = (uintptr_t)__builtin_thread_pointer();
#elif defined(__riscv)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// tp is Thread Pointer provided by the RISC-V ABI.
__asm__ ("mv %0, tp" : "=r" (tid));
#endif
#else
// Fallback to a portable implementation if we do not have a faster
// platform-specific implementation.
tid = _Py_GetThreadLocal_Addr();
#endif
return tid;
}
static inline Py_ALWAYS_INLINE int
_Py_IsOwnedByCurrentThread(PyObject *ob)
{
#ifdef _Py_THREAD_SANITIZER
return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId();
#else
return ob->ob_tid == _Py_ThreadId();
#endif
}
#endif

View file

@ -113,8 +113,10 @@ struct PyModuleDef_Slot {
# define Py_MOD_GIL_NOT_USED ((void *)1)
#endif
#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED)
#if !defined(Py_LIMITED_API)
# if defined(Py_GIL_DISABLED)
PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil);
# endif
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)

View file

@ -186,85 +186,6 @@ typedef struct PyVarObject PyVarObject;
PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y);
#define Py_Is(x, y) ((x) == (y))
#if defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
PyAPI_FUNC(uintptr_t) _Py_GetThreadLocal_Addr(void);
static inline uintptr_t
_Py_ThreadId(void)
{
uintptr_t tid;
#if defined(_MSC_VER) && defined(_M_X64)
tid = __readgsqword(48);
#elif defined(_MSC_VER) && defined(_M_IX86)
tid = __readfsdword(24);
#elif defined(_MSC_VER) && defined(_M_ARM64)
tid = __getReg(18);
#elif defined(__MINGW32__) && defined(_M_X64)
tid = __readgsqword(48);
#elif defined(__MINGW32__) && defined(_M_IX86)
tid = __readfsdword(24);
#elif defined(__MINGW32__) && defined(_M_ARM64)
tid = __getReg(18);
#elif defined(__i386__)
__asm__("{movl %%gs:0, %0|mov %0, dword ptr gs:[0]}" : "=r" (tid)); // 32-bit always uses GS
#elif defined(__MACH__) && defined(__x86_64__)
__asm__("{movq %%gs:0, %0|mov %0, qword ptr gs:[0]}" : "=r" (tid)); // x86_64 macOSX uses GS
#elif defined(__x86_64__)
__asm__("{movq %%fs:0, %0|mov %0, qword ptr fs:[0]}" : "=r" (tid)); // x86_64 Linux, BSD uses FS
#elif defined(__arm__) && __ARM_ARCH >= 7
__asm__ ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tid));
#elif defined(__aarch64__) && defined(__APPLE__)
__asm__ ("mrs %0, tpidrro_el0" : "=r" (tid));
#elif defined(__aarch64__)
__asm__ ("mrs %0, tpidr_el0" : "=r" (tid));
#elif defined(__powerpc64__)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// r13 is reserved for use as system thread ID by the Power 64-bit ABI.
register uintptr_t tp __asm__ ("r13");
__asm__("" : "=r" (tp));
tid = tp;
#endif
#elif defined(__powerpc__)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// r2 is reserved for use as system thread ID by the Power 32-bit ABI.
register uintptr_t tp __asm__ ("r2");
__asm__ ("" : "=r" (tp));
tid = tp;
#endif
#elif defined(__s390__) && defined(__GNUC__)
// Both GCC and Clang have supported __builtin_thread_pointer
// for s390 from long time ago.
tid = (uintptr_t)__builtin_thread_pointer();
#elif defined(__riscv)
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
tid = (uintptr_t)__builtin_thread_pointer();
#else
// tp is Thread Pointer provided by the RISC-V ABI.
__asm__ ("mv %0, tp" : "=r" (tid));
#endif
#else
// Fallback to a portable implementation if we do not have a faster
// platform-specific implementation.
tid = _Py_GetThreadLocal_Addr();
#endif
return tid;
}
static inline Py_ALWAYS_INLINE int
_Py_IsOwnedByCurrentThread(PyObject *ob)
{
#ifdef _Py_THREAD_SANITIZER
return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId();
#else
return ob->ob_tid == _Py_ThreadId();
#endif
}
#endif
PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;
@ -652,8 +573,10 @@ given type object has a specified feature.
#define _Py_IMMORTAL_FLAGS (1 << 0)
#define _Py_LEGACY_ABI_CHECK_FLAG (1 << 1) /* see PyModuleDef_Init() */
#define _Py_STATICALLY_ALLOCATED_FLAG (1 << 2)
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
#define _Py_TYPE_REVEALED_FLAG (1 << 3)
#if !defined(Py_LIMITED_API)
# if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
# define _Py_TYPE_REVEALED_FLAG (1 << 3)
# endif
#endif
#define Py_CONSTANT_NONE 0

View file

@ -77,6 +77,14 @@
# define Py_BUILD_CORE
#endif
#if defined(Py_TARGET_ABI3T)
# if !defined(Py_GIL_DISABLED)
// Define Py_GIL_DISABLED for users' needs. This macro is used to enable
// locking needed in for free-threaded interpreters builds.
# define Py_GIL_DISABLED
# endif
#endif
/**************************************************************************
Symbols and macros to supply platform-independent interfaces to basic

View file

@ -5,6 +5,7 @@ extern "C" {
#endif
#if !defined(_Py_OPAQUE_PYOBJECT)
/*
Immortalization:
@ -90,14 +91,16 @@ check by comparing the reference count field to the minimum immortality refcount
# define _Py_REF_SHARED(refcnt, flags) \
(((refcnt) << _Py_REF_SHARED_SHIFT) + (flags))
#endif // Py_GIL_DISABLED
#endif // _Py_OPAQUE_PYOBJECT
// Py_REFCNT() implementation for the stable ABI
PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob);
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030e0000
// Stable ABI implements Py_REFCNT() as a function call
// on limited C API version 3.14 and newer.
// on limited C API version 3.14 and newer, and on abi3t.
#elif defined(_Py_OPAQUE_PYOBJECT)
// Py_REFCNT() is also a function call in abi3t.
#else
static inline Py_ssize_t _Py_REFCNT(PyObject *ob) {
#if !defined(Py_GIL_DISABLED)
@ -150,9 +153,10 @@ PyAPI_FUNC(void) _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt);
static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
assert(refcnt >= 0);
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030d0000
#if (defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030d0000) \
|| defined(_Py_OPAQUE_PYOBJECT)
// Stable ABI implements Py_SET_REFCNT() as a function call
// on limited C API version 3.13 and newer.
// on limited C API version 3.13 and newer, and abi3t.
_Py_SetRefcnt(ob, refcnt);
#else
// This immortal check is for code that is unaware of immortal objects.
@ -191,7 +195,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
ob->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
}
#endif // Py_GIL_DISABLED
#endif // Py_LIMITED_API+0 < 0x030d0000
#endif // Py_LIMITED_API
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_REFCNT(ob, refcnt) Py_SET_REFCNT(_PyObject_CAST(ob), (refcnt))
@ -250,10 +254,11 @@ PyAPI_FUNC(void) _Py_DecRef(PyObject *);
static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
{
#if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))
#if (defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))) \
|| defined(_Py_OPAQUE_PYOBJECT)
// Stable ABI implements Py_INCREF() as a function call on limited C API
// version 3.12 and newer, and on Python built in debug mode. _Py_IncRef()
// was added to Python 3.10.0a7, use Py_IncRef() on older Python versions.
// version 3.12 and newer, abi3t, and on Python built in debug mode.
// _Py_IncRef() was added to Python 3.10.0a7, use Py_IncRef() on older versions.
// Py_IncRef() accepts NULL whereas _Py_IncRef() doesn't.
# if Py_LIMITED_API+0 >= 0x030a00A7
_Py_IncRef(op);
@ -305,8 +310,8 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
# define Py_INCREF(op) Py_INCREF(_PyObject_CAST(op))
#endif
#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED)
#if !defined(Py_LIMITED_API)
#if defined(Py_GIL_DISABLED)
// Implements Py_DECREF on objects not owned by the current thread.
PyAPI_FUNC(void) _Py_DecRefShared(PyObject *);
PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
@ -316,12 +321,14 @@ PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
// zero. Otherwise, the thread gives up ownership and merges the reference
// count fields.
PyAPI_FUNC(void) _Py_MergeZeroLocalRefcount(PyObject *);
#endif
#endif // Py_GIL_DISABLED
#endif // Py_LIMITED_API
#if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))
#if (defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))) \
|| defined(_Py_OPAQUE_PYOBJECT)
// Stable ABI implements Py_DECREF() as a function call on limited C API
// version 3.12 and newer, and on Python built in debug mode. _Py_DecRef() was
// added to Python 3.10.0a7, use Py_DecRef() on older Python versions.
// version 3.12 and newer, abi3t, and on Python built in debug mode.
// _Py_DecRef() was added to Python 3.10.0a7, use Py_DecRef() on older versions.
// Py_DecRef() accepts NULL whereas _Py_DecRef() doesn't.
static inline void Py_DECREF(PyObject *op) {
# if Py_LIMITED_API+0 >= 0x030a00A7