summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-04-13 09:38:42 (GMT)
committerGitHub <noreply@github.com>2020-04-13 09:38:42 (GMT)
commit0135598d729d01f35ce08d47160adaa095a6149f (patch)
treeb272b49c6e2ed7b5270f9aeb565fe61a5ff9d634
parent85dd6bb1f61f7edcd6ac0b640a98644531690a0e (diff)
downloadcpython-0135598d729d01f35ce08d47160adaa095a6149f.zip
cpython-0135598d729d01f35ce08d47160adaa095a6149f.tar.gz
cpython-0135598d729d01f35ce08d47160adaa095a6149f.tar.bz2
bpo-40241: Add pycore_gc.h header file (GH-19494)
Move the PyGC_Head structure and the following private macros to the internal C API: * _PyGCHead_FINALIZED() * _PyGCHead_NEXT() * _PyGCHead_PREV() * _PyGCHead_SET_FINALIZED() * _PyGCHead_SET_NEXT() * _PyGCHead_SET_PREV() * _PyGC_FINALIZED() * _PyGC_PREV_MASK * _PyGC_PREV_MASK_COLLECTING * _PyGC_PREV_MASK_FINALIZED * _PyGC_PREV_SHIFT * _PyGC_SET_FINALIZED() * _PyObject_GC_IS_TRACKED() * _PyObject_GC_MAY_BE_TRACKED() * _Py_AS_GC(o) Keep the private _PyGC_FINALIZED() macro in the public C API for backward compatibility with Python 3.8: make it an alias to the new PyObject_GC_IsFinalized() function. Move the SIZEOF_PYGC_HEAD constant from _testcapi module to _testinternalcapi module.
-rw-r--r--Include/cpython/objimpl.h61
-rw-r--r--Include/internal/pycore_gc.h69
-rw-r--r--Include/internal/pycore_pymem.h3
-rw-r--r--Lib/test/support/__init__.py4
-rw-r--r--Lib/test/test_sys.py4
-rw-r--r--Misc/NEWS.d/next/C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst1
-rw-r--r--Modules/_testcapimodule.c1
-rw-r--r--Modules/_testinternalcapi.c17
8 files changed, 98 insertions, 62 deletions
diff --git a/Include/cpython/objimpl.h b/Include/cpython/objimpl.h
index 832622c..6634f29 100644
--- a/Include/cpython/objimpl.h
+++ b/Include/cpython/objimpl.h
@@ -125,61 +125,12 @@ PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void);
(PyType_IS_GC(Py_TYPE(o)) \
&& (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o)))
-/* GC information is stored BEFORE the object structure. */
-typedef struct {
- // Pointer to next object in the list.
- // 0 means the object is not tracked
- uintptr_t _gc_next;
-
- // Pointer to previous object in the list.
- // Lowest two bits are used for flags documented later.
- uintptr_t _gc_prev;
-} PyGC_Head;
-
-#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
-
-/* True if the object is currently tracked by the GC. */
-#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0)
-
-/* True if the object may be tracked by the GC in the future, or already is.
- This can be useful to implement some optimizations. */
-#define _PyObject_GC_MAY_BE_TRACKED(obj) \
- (PyObject_IS_GC(obj) && \
- (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj)))
-
-
-/* Bit flags for _gc_prev */
-/* Bit 0 is set when tp_finalize is called */
-#define _PyGC_PREV_MASK_FINALIZED (1)
-/* Bit 1 is set when the object is in generation which is GCed currently. */
-#define _PyGC_PREV_MASK_COLLECTING (2)
-/* The (N-2) most significant bits contain the real address. */
-#define _PyGC_PREV_SHIFT (2)
-#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
-
-// Lowest bit of _gc_next is used for flags only in GC.
-// But it is always 0 for normal code.
-#define _PyGCHead_NEXT(g) ((PyGC_Head*)(g)->_gc_next)
-#define _PyGCHead_SET_NEXT(g, p) ((g)->_gc_next = (uintptr_t)(p))
-
-// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.
-#define _PyGCHead_PREV(g) ((PyGC_Head*)((g)->_gc_prev & _PyGC_PREV_MASK))
-#define _PyGCHead_SET_PREV(g, p) do { \
- assert(((uintptr_t)p & ~_PyGC_PREV_MASK) == 0); \
- (g)->_gc_prev = ((g)->_gc_prev & ~_PyGC_PREV_MASK) \
- | ((uintptr_t)(p)); \
- } while (0)
-
-#define _PyGCHead_FINALIZED(g) \
- (((g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0)
-#define _PyGCHead_SET_FINALIZED(g) \
- ((g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED)
-
-#define _PyGC_FINALIZED(o) \
- _PyGCHead_FINALIZED(_Py_AS_GC(o))
-#define _PyGC_SET_FINALIZED(o) \
- _PyGCHead_SET_FINALIZED(_Py_AS_GC(o))
-
+/* Code built with Py_BUILD_CORE must include pycore_gc.h instead which
+ defines a different _PyGC_FINALIZED() macro. */
+#ifndef Py_BUILD_CORE
+ // Kept for backward compatibility with Python 3.8
+# define _PyGC_FINALIZED(o) PyObject_GC_IsFinalized(o)
+#endif
PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size);
PyAPI_FUNC(PyObject *) _PyObject_GC_Calloc(size_t size);
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
new file mode 100644
index 0000000..7309205
--- /dev/null
+++ b/Include/internal/pycore_gc.h
@@ -0,0 +1,69 @@
+#ifndef Py_INTERNAL_GC_H
+#define Py_INTERNAL_GC_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+/* GC information is stored BEFORE the object structure. */
+typedef struct {
+ // Pointer to next object in the list.
+ // 0 means the object is not tracked
+ uintptr_t _gc_next;
+
+ // Pointer to previous object in the list.
+ // Lowest two bits are used for flags documented later.
+ uintptr_t _gc_prev;
+} PyGC_Head;
+
+#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
+
+/* True if the object is currently tracked by the GC. */
+#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0)
+
+/* True if the object may be tracked by the GC in the future, or already is.
+ This can be useful to implement some optimizations. */
+#define _PyObject_GC_MAY_BE_TRACKED(obj) \
+ (PyObject_IS_GC(obj) && \
+ (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj)))
+
+
+/* Bit flags for _gc_prev */
+/* Bit 0 is set when tp_finalize is called */
+#define _PyGC_PREV_MASK_FINALIZED (1)
+/* Bit 1 is set when the object is in generation which is GCed currently. */
+#define _PyGC_PREV_MASK_COLLECTING (2)
+/* The (N-2) most significant bits contain the real address. */
+#define _PyGC_PREV_SHIFT (2)
+#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
+
+// Lowest bit of _gc_next is used for flags only in GC.
+// But it is always 0 for normal code.
+#define _PyGCHead_NEXT(g) ((PyGC_Head*)(g)->_gc_next)
+#define _PyGCHead_SET_NEXT(g, p) ((g)->_gc_next = (uintptr_t)(p))
+
+// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.
+#define _PyGCHead_PREV(g) ((PyGC_Head*)((g)->_gc_prev & _PyGC_PREV_MASK))
+#define _PyGCHead_SET_PREV(g, p) do { \
+ assert(((uintptr_t)p & ~_PyGC_PREV_MASK) == 0); \
+ (g)->_gc_prev = ((g)->_gc_prev & ~_PyGC_PREV_MASK) \
+ | ((uintptr_t)(p)); \
+ } while (0)
+
+#define _PyGCHead_FINALIZED(g) \
+ (((g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0)
+#define _PyGCHead_SET_FINALIZED(g) \
+ ((g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED)
+
+#define _PyGC_FINALIZED(o) \
+ _PyGCHead_FINALIZED(_Py_AS_GC(o))
+#define _PyGC_SET_FINALIZED(o) \
+ _PyGCHead_SET_FINALIZED(_Py_AS_GC(o))
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_GC_H */
diff --git a/Include/internal/pycore_pymem.h b/Include/internal/pycore_pymem.h
index db153e0..34a17d5 100644
--- a/Include/internal/pycore_pymem.h
+++ b/Include/internal/pycore_pymem.h
@@ -8,7 +8,8 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif
-#include "pymem.h" /* PyMemAllocatorName */
+#include "pymem.h" // PyMemAllocatorName
+#include "pycore_gc.h" // PyGC_Head
/* GC runtime state */
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 1f792d8..9f43b40 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -1764,12 +1764,12 @@ _TPFLAGS_HAVE_GC = 1<<14
_TPFLAGS_HEAPTYPE = 1<<9
def check_sizeof(test, o, size):
- import _testcapi
+ import _testinternalcapi
result = sys.getsizeof(o)
# add GC header size
if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\
((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))):
- size += _testcapi.SIZEOF_PYGC_HEAD
+ size += _testinternalcapi.SIZEOF_PYGC_HEAD
msg = 'wrong size for %s: got %d, expected %d' \
% (type(o), result, size)
test.assertEqual(result, size, msg)
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 3957258..329f7dd 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1056,8 +1056,8 @@ class SizeofTest(unittest.TestCase):
def setUp(self):
self.P = struct.calcsize('P')
self.longdigit = sys.int_info.sizeof_digit
- import _testcapi
- self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD
+ import _testinternalcapi
+ self.gc_headsize = _testinternalcapi.SIZEOF_PYGC_HEAD
check_sizeof = test.support.check_sizeof
diff --git a/Misc/NEWS.d/next/C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst b/Misc/NEWS.d/next/C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst
new file mode 100644
index 0000000..b3e4aaf
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst
@@ -0,0 +1 @@
+Move the :c:type:`PyGC_Head` structure to the internal C API.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 0a30fea..c97cbe8 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -6716,7 +6716,6 @@ PyInit__testcapi(void)
PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(ULLONG_MAX));
PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX));
PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN));
- PyModule_AddObject(m, "SIZEOF_PYGC_HEAD", PyLong_FromSsize_t(sizeof(PyGC_Head)));
PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t)));
Py_INCREF(&PyInstanceMethod_Type);
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index 394b870..8352f6e 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -10,6 +10,7 @@
#include "Python.h"
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
+#include "pycore_gc.h" // PyGC_Head
static PyObject *
@@ -52,5 +53,19 @@ static struct PyModuleDef _testcapimodule = {
PyMODINIT_FUNC
PyInit__testinternalcapi(void)
{
- return PyModule_Create(&_testcapimodule);
+ PyObject *module = PyModule_Create(&_testcapimodule);
+ if (module == NULL) {
+ return NULL;
+ }
+
+ if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
+ PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
+ goto error;
+ }
+
+ return module;
+
+error:
+ Py_DECREF(module);
+ return NULL;
}