Skip to content

Commit cb3c6fe

Browse files
committed
gh-146636: abi3t: Define Py_GIL_DISABLED but make sure not to use it
When compiling for abi3t, define Py_GIL_DISABLED, so that users who check it to enable additional locking aren't broken. But also add a test that Python headers themselves don't use Py_GIL_DISABLED -- abi3 and abi3t ought to be the same except the _Py_OPAQUE_PYOBJECT differences. This is done using the GCC-only poison pragma. This did require rewriting some preprocessor conditions, and moving _Py_IsOwnedByCurrentThread & supporting functions to a cpython/ header.
1 parent bce96a1 commit cb3c6fe

File tree

6 files changed

+131
-96
lines changed

6 files changed

+131
-96
lines changed

Include/cpython/object.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,3 +495,82 @@ PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *);
495495
PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *);
496496

497497
PyAPI_FUNC(int) PyUnstable_SetImmortal(PyObject *op);
498+
499+
#if defined(Py_GIL_DISABLED)
500+
PyAPI_FUNC(uintptr_t) _Py_GetThreadLocal_Addr(void);
501+
502+
static inline uintptr_t
503+
_Py_ThreadId(void)
504+
{
505+
uintptr_t tid;
506+
#if defined(_MSC_VER) && defined(_M_X64)
507+
tid = __readgsqword(48);
508+
#elif defined(_MSC_VER) && defined(_M_IX86)
509+
tid = __readfsdword(24);
510+
#elif defined(_MSC_VER) && defined(_M_ARM64)
511+
tid = __getReg(18);
512+
#elif defined(__MINGW32__) && defined(_M_X64)
513+
tid = __readgsqword(48);
514+
#elif defined(__MINGW32__) && defined(_M_IX86)
515+
tid = __readfsdword(24);
516+
#elif defined(__MINGW32__) && defined(_M_ARM64)
517+
tid = __getReg(18);
518+
#elif defined(__i386__)
519+
__asm__("{movl %%gs:0, %0|mov %0, dword ptr gs:[0]}" : "=r" (tid)); // 32-bit always uses GS
520+
#elif defined(__MACH__) && defined(__x86_64__)
521+
__asm__("{movq %%gs:0, %0|mov %0, qword ptr gs:[0]}" : "=r" (tid)); // x86_64 macOSX uses GS
522+
#elif defined(__x86_64__)
523+
__asm__("{movq %%fs:0, %0|mov %0, qword ptr fs:[0]}" : "=r" (tid)); // x86_64 Linux, BSD uses FS
524+
#elif defined(__arm__) && __ARM_ARCH >= 7
525+
__asm__ ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tid));
526+
#elif defined(__aarch64__) && defined(__APPLE__)
527+
__asm__ ("mrs %0, tpidrro_el0" : "=r" (tid));
528+
#elif defined(__aarch64__)
529+
__asm__ ("mrs %0, tpidr_el0" : "=r" (tid));
530+
#elif defined(__powerpc64__)
531+
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
532+
tid = (uintptr_t)__builtin_thread_pointer();
533+
#else
534+
// r13 is reserved for use as system thread ID by the Power 64-bit ABI.
535+
register uintptr_t tp __asm__ ("r13");
536+
__asm__("" : "=r" (tp));
537+
tid = tp;
538+
#endif
539+
#elif defined(__powerpc__)
540+
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
541+
tid = (uintptr_t)__builtin_thread_pointer();
542+
#else
543+
// r2 is reserved for use as system thread ID by the Power 32-bit ABI.
544+
register uintptr_t tp __asm__ ("r2");
545+
__asm__ ("" : "=r" (tp));
546+
tid = tp;
547+
#endif
548+
#elif defined(__s390__) && defined(__GNUC__)
549+
// Both GCC and Clang have supported __builtin_thread_pointer
550+
// for s390 from long time ago.
551+
tid = (uintptr_t)__builtin_thread_pointer();
552+
#elif defined(__riscv)
553+
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
554+
tid = (uintptr_t)__builtin_thread_pointer();
555+
#else
556+
// tp is Thread Pointer provided by the RISC-V ABI.
557+
__asm__ ("mv %0, tp" : "=r" (tid));
558+
#endif
559+
#else
560+
// Fallback to a portable implementation if we do not have a faster
561+
// platform-specific implementation.
562+
tid = _Py_GetThreadLocal_Addr();
563+
#endif
564+
return tid;
565+
}
566+
567+
static inline Py_ALWAYS_INLINE int
568+
_Py_IsOwnedByCurrentThread(PyObject *ob)
569+
{
570+
#ifdef _Py_THREAD_SANITIZER
571+
return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId();
572+
#else
573+
return ob->ob_tid == _Py_ThreadId();
574+
#endif
575+
}
576+
#endif

Include/moduleobject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ struct PyModuleDef_Slot {
113113
# define Py_MOD_GIL_NOT_USED ((void *)1)
114114
#endif
115115

116-
#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED)
116+
#if !defined(Py_LIMITED_API)
117+
# if defined(Py_GIL_DISABLED)
117118
PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil);
119+
# endif
118120
#endif
119121

120122
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)

Include/object.h

Lines changed: 4 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -186,85 +186,6 @@ typedef struct PyVarObject PyVarObject;
186186
PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y);
187187
#define Py_Is(x, y) ((x) == (y))
188188

189-
#if defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
190-
PyAPI_FUNC(uintptr_t) _Py_GetThreadLocal_Addr(void);
191-
192-
static inline uintptr_t
193-
_Py_ThreadId(void)
194-
{
195-
uintptr_t tid;
196-
#if defined(_MSC_VER) && defined(_M_X64)
197-
tid = __readgsqword(48);
198-
#elif defined(_MSC_VER) && defined(_M_IX86)
199-
tid = __readfsdword(24);
200-
#elif defined(_MSC_VER) && defined(_M_ARM64)
201-
tid = __getReg(18);
202-
#elif defined(__MINGW32__) && defined(_M_X64)
203-
tid = __readgsqword(48);
204-
#elif defined(__MINGW32__) && defined(_M_IX86)
205-
tid = __readfsdword(24);
206-
#elif defined(__MINGW32__) && defined(_M_ARM64)
207-
tid = __getReg(18);
208-
#elif defined(__i386__)
209-
__asm__("{movl %%gs:0, %0|mov %0, dword ptr gs:[0]}" : "=r" (tid)); // 32-bit always uses GS
210-
#elif defined(__MACH__) && defined(__x86_64__)
211-
__asm__("{movq %%gs:0, %0|mov %0, qword ptr gs:[0]}" : "=r" (tid)); // x86_64 macOSX uses GS
212-
#elif defined(__x86_64__)
213-
__asm__("{movq %%fs:0, %0|mov %0, qword ptr fs:[0]}" : "=r" (tid)); // x86_64 Linux, BSD uses FS
214-
#elif defined(__arm__) && __ARM_ARCH >= 7
215-
__asm__ ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tid));
216-
#elif defined(__aarch64__) && defined(__APPLE__)
217-
__asm__ ("mrs %0, tpidrro_el0" : "=r" (tid));
218-
#elif defined(__aarch64__)
219-
__asm__ ("mrs %0, tpidr_el0" : "=r" (tid));
220-
#elif defined(__powerpc64__)
221-
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
222-
tid = (uintptr_t)__builtin_thread_pointer();
223-
#else
224-
// r13 is reserved for use as system thread ID by the Power 64-bit ABI.
225-
register uintptr_t tp __asm__ ("r13");
226-
__asm__("" : "=r" (tp));
227-
tid = tp;
228-
#endif
229-
#elif defined(__powerpc__)
230-
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
231-
tid = (uintptr_t)__builtin_thread_pointer();
232-
#else
233-
// r2 is reserved for use as system thread ID by the Power 32-bit ABI.
234-
register uintptr_t tp __asm__ ("r2");
235-
__asm__ ("" : "=r" (tp));
236-
tid = tp;
237-
#endif
238-
#elif defined(__s390__) && defined(__GNUC__)
239-
// Both GCC and Clang have supported __builtin_thread_pointer
240-
// for s390 from long time ago.
241-
tid = (uintptr_t)__builtin_thread_pointer();
242-
#elif defined(__riscv)
243-
#if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer)
244-
tid = (uintptr_t)__builtin_thread_pointer();
245-
#else
246-
// tp is Thread Pointer provided by the RISC-V ABI.
247-
__asm__ ("mv %0, tp" : "=r" (tid));
248-
#endif
249-
#else
250-
// Fallback to a portable implementation if we do not have a faster
251-
// platform-specific implementation.
252-
tid = _Py_GetThreadLocal_Addr();
253-
#endif
254-
return tid;
255-
}
256-
257-
static inline Py_ALWAYS_INLINE int
258-
_Py_IsOwnedByCurrentThread(PyObject *ob)
259-
{
260-
#ifdef _Py_THREAD_SANITIZER
261-
return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId();
262-
#else
263-
return ob->ob_tid == _Py_ThreadId();
264-
#endif
265-
}
266-
#endif
267-
268189
PyAPI_DATA(PyTypeObject) PyLong_Type;
269190
PyAPI_DATA(PyTypeObject) PyBool_Type;
270191

@@ -652,8 +573,10 @@ given type object has a specified feature.
652573
#define _Py_IMMORTAL_FLAGS (1 << 0)
653574
#define _Py_LEGACY_ABI_CHECK_FLAG (1 << 1) /* see PyModuleDef_Init() */
654575
#define _Py_STATICALLY_ALLOCATED_FLAG (1 << 2)
655-
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
656-
#define _Py_TYPE_REVEALED_FLAG (1 << 3)
576+
#if !defined(Py_LIMITED_API)
577+
# if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
578+
# define _Py_TYPE_REVEALED_FLAG (1 << 3)
579+
# endif
657580
#endif
658581

659582
#define Py_CONSTANT_NONE 0

Include/pyport.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@
7777
# define Py_BUILD_CORE
7878
#endif
7979

80+
#if defined(Py_TARGET_ABI3T)
81+
# if !defined(Py_GIL_DISABLED)
82+
// Define Py_GIL_DISABLED for users' needs. This macro is used to enable
83+
// locking needed in for free-threaded interpreters builds.
84+
# define Py_GIL_DISABLED
85+
# endif
86+
# if defined(_Py_IS_TESTCEXT)
87+
// When compiling for abi3t, contents of Python.h should not depend
88+
// on Py_GIL_DISABLED.
89+
// We ask GCC to error if it sees the macro from this point on.
90+
// Since users are free to the macro, and there's no way to undo the poisoning
91+
// at the end of Python.h, we only do this in a test module.
92+
# ifdef __GNUC__
93+
# undef Py_GIL_DISABLED
94+
# pragma GCC poison Py_GIL_DISABLED
95+
# endif
96+
# endif
97+
#endif
98+
8099

81100
/**************************************************************************
82101
Symbols and macros to supply platform-independent interfaces to basic

Include/refcount.h

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ extern "C" {
55
#endif
66

77

8+
#if !defined(_Py_OPAQUE_PYOBJECT)
89
/*
910
Immortalization:
1011
@@ -90,14 +91,16 @@ check by comparing the reference count field to the minimum immortality refcount
9091
# define _Py_REF_SHARED(refcnt, flags) \
9192
(((refcnt) << _Py_REF_SHARED_SHIFT) + (flags))
9293
#endif // Py_GIL_DISABLED
93-
94+
#endif // _Py_OPAQUE_PYOBJECT
9495

9596
// Py_REFCNT() implementation for the stable ABI
9697
PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob);
9798

9899
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030e0000
99100
// Stable ABI implements Py_REFCNT() as a function call
100-
// on limited C API version 3.14 and newer.
101+
// on limited C API version 3.14 and newer, and on abi3t.
102+
#elif defined(_Py_OPAQUE_PYOBJECT)
103+
// Py_REFCNT() is also a function call in abi3t.
101104
#else
102105
static inline Py_ssize_t _Py_REFCNT(PyObject *ob) {
103106
#if !defined(Py_GIL_DISABLED)
@@ -150,9 +153,10 @@ PyAPI_FUNC(void) _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt);
150153

151154
static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
152155
assert(refcnt >= 0);
153-
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030d0000
156+
#if (defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030d0000) \
157+
|| defined(_Py_OPAQUE_PYOBJECT)
154158
// Stable ABI implements Py_SET_REFCNT() as a function call
155-
// on limited C API version 3.13 and newer.
159+
// on limited C API version 3.13 and newer, and abi3t.
156160
_Py_SetRefcnt(ob, refcnt);
157161
#else
158162
// 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) {
191195
ob->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
192196
}
193197
#endif // Py_GIL_DISABLED
194-
#endif // Py_LIMITED_API+0 < 0x030d0000
198+
#endif // Py_LIMITED_API
195199
}
196200
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
197201
# define Py_SET_REFCNT(ob, refcnt) Py_SET_REFCNT(_PyObject_CAST(ob), (refcnt))
@@ -250,10 +254,11 @@ PyAPI_FUNC(void) _Py_DecRef(PyObject *);
250254

251255
static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
252256
{
253-
#if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))
257+
#if (defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))) \
258+
|| defined(_Py_OPAQUE_PYOBJECT)
254259
// Stable ABI implements Py_INCREF() as a function call on limited C API
255-
// version 3.12 and newer, and on Python built in debug mode. _Py_IncRef()
256-
// was added to Python 3.10.0a7, use Py_IncRef() on older Python versions.
260+
// version 3.12 and newer, abi3t, and on Python built in debug mode.
261+
// _Py_IncRef() was added to Python 3.10.0a7, use Py_IncRef() on older versions.
257262
// Py_IncRef() accepts NULL whereas _Py_IncRef() doesn't.
258263
# if Py_LIMITED_API+0 >= 0x030a00A7
259264
_Py_IncRef(op);
@@ -305,8 +310,8 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
305310
# define Py_INCREF(op) Py_INCREF(_PyObject_CAST(op))
306311
#endif
307312

308-
309-
#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED)
313+
#if !defined(Py_LIMITED_API)
314+
#if defined(Py_GIL_DISABLED)
310315
// Implements Py_DECREF on objects not owned by the current thread.
311316
PyAPI_FUNC(void) _Py_DecRefShared(PyObject *);
312317
PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
@@ -316,12 +321,14 @@ PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
316321
// zero. Otherwise, the thread gives up ownership and merges the reference
317322
// count fields.
318323
PyAPI_FUNC(void) _Py_MergeZeroLocalRefcount(PyObject *);
319-
#endif
324+
#endif // Py_GIL_DISABLED
325+
#endif // Py_LIMITED_API
320326

321-
#if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))
327+
#if (defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))) \
328+
|| defined(_Py_OPAQUE_PYOBJECT)
322329
// Stable ABI implements Py_DECREF() as a function call on limited C API
323-
// version 3.12 and newer, and on Python built in debug mode. _Py_DecRef() was
324-
// added to Python 3.10.0a7, use Py_DecRef() on older Python versions.
330+
// version 3.12 and newer, abi3t, and on Python built in debug mode.
331+
// _Py_DecRef() was added to Python 3.10.0a7, use Py_DecRef() on older versions.
325332
// Py_DecRef() accepts NULL whereas _Py_DecRef() doesn't.
326333
static inline void Py_DECREF(PyObject *op) {
327334
# if Py_LIMITED_API+0 >= 0x030a00A7

Lib/test/test_cext/setup.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
# The purpose of test_cext extension is to check that building a C
1919
# extension using the Python C API does not emit C compiler warnings.
2020
'-Werror',
21+
# Enable extra checks for header files, which:
22+
# - need to be enabled somewhere inside Python headers (rather than
23+
# before including Python.h)
24+
# - should not be checked for user code
25+
'-D_Py_IS_TESTCEXT',
2126
]
2227

2328
# C compiler flags for GCC and clang

0 commit comments

Comments
 (0)