From 0135598d729d01f35ce08d47160adaa095a6149f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 13 Apr 2020 11:38:42 +0200 Subject: 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. --- Include/cpython/objimpl.h | 61 ++----------------- Include/internal/pycore_gc.h | 69 ++++++++++++++++++++++ Include/internal/pycore_pymem.h | 3 +- Lib/test/support/__init__.py | 4 +- Lib/test/test_sys.py | 4 +- .../C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst | 1 + Modules/_testcapimodule.c | 1 - Modules/_testinternalcapi.c | 17 +++++- 8 files changed, 98 insertions(+), 62 deletions(-) create mode 100644 Include/internal/pycore_gc.h create mode 100644 Misc/NEWS.d/next/C API/2020-04-13-02-56-24.bpo-40241._FOf7E.rst 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; } -- cgit v0.12