Skip to content

bpo-35081: Make some _PyGC macros internal #10507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions Doc/c-api/gcsupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ Constructors for container types must conform to two rules:
end of the constructor.


.. c:function:: void _PyObject_GC_TRACK(PyObject *op)

A macro version of :c:func:`PyObject_GC_Track`. It should not be used for
extension modules.

Similarly, the deallocator for the object must conform to a similar pair of
rules:

Expand All @@ -90,10 +85,10 @@ rules:
the fields used by the :c:member:`~PyTypeObject.tp_traverse` handler become invalid.


.. c:function:: void _PyObject_GC_UNTRACK(PyObject *op)
.. versionchanged:: 3.8

A macro version of :c:func:`PyObject_GC_UnTrack`. It should not be used for
extension modules.
The :c:func:`_PyObject_GC_TRACK` and :c:func:`_PyObject_GC_UNTRACK` macros
have been removed from the public C API.

The :c:member:`~PyTypeObject.tp_traverse` handler accepts a function parameter of this type:

Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_pymem.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,6 @@ struct _gc_runtime_state {

PyAPI_FUNC(void) _PyGC_Initialize(struct _gc_runtime_state *);

#define _PyGC_generation0 _PyRuntime.gc.generation0


/* Set the memory allocator of the specified domain to the default.
Save the old allocator into *old_alloc if it's non-NULL.
Expand Down
73 changes: 46 additions & 27 deletions Include/objimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,18 +256,18 @@ PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void);
/* Test if a type has a GC head */
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)

/* Test if an object has a GC head */
#ifndef Py_LIMITED_API
#define PyObject_IS_GC(o) (PyType_IS_GC(Py_TYPE(o)) && \
(Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o)))
#endif

PyAPI_FUNC(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, Py_ssize_t);
#define PyObject_GC_Resize(type, op, n) \
( (type *) _PyObject_GC_Resize((PyVarObject *)(op), (n)) )

/* GC information is stored BEFORE the object structure. */

#ifndef Py_LIMITED_API
/* Test if an object has a GC head */
#define PyObject_IS_GC(o) \
(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
Expand All @@ -278,10 +278,21 @@ typedef struct {
uintptr_t _gc_prev;
} PyGC_Head;

extern PyGC_Head *_PyGC_generation0;

#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)))
#endif


#if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN)

/* Bit flags for _gc_prev */
/* Bit 0 is set when tp_finalize is called */
#define _PyGC_PREV_MASK_FINALIZED (1)
Expand All @@ -304,38 +315,46 @@ extern PyGC_Head *_PyGC_generation0;
| ((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 _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))
#define _PyGC_FINALIZED(o) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this internal broke Cython (cython/cython#2721). It's used in the finaliser implementation, to determine if an object for which tp_dealloc() is called has already been finalised or whether we have to do it. I'm not sure how to deal with this on our side now. Any clue?

_PyGCHead_FINALIZED(_Py_AS_GC(o))
#define _PyGC_SET_FINALIZED(o) \
_PyGCHead_SET_FINALIZED(_Py_AS_GC(o))

/* Tell the GC to track this object.
*
* NB: While the object is tracked by the collector, it must be safe to call the
* ob_traverse method.
*
* Internal note: _PyGC_generation0->_gc_prev doesn't have any bit flags
* Internal note: _PyRuntime.gc.generation0->_gc_prev doesn't have any bit flags
* because it's not object header. So we don't use _PyGCHead_PREV() and
* _PyGCHead_SET_PREV() for it to avoid unnecessary bitwise operations.
*
* The PyObject_GC_Track() function is the public version of this macro.
*/
#define _PyObject_GC_TRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
if (g->_gc_next != 0) { \
Py_FatalError("GC object already tracked"); \
} \
assert((g->_gc_prev & _PyGC_PREV_MASK_COLLECTING) == 0); \
PyGC_Head *last = (PyGC_Head*)(_PyGC_generation0->_gc_prev); \
PyGC_Head *last = (PyGC_Head*)(_PyRuntime.gc.generation0->_gc_prev); \
_PyGCHead_SET_NEXT(last, g); \
_PyGCHead_SET_PREV(g, last); \
_PyGCHead_SET_NEXT(g, _PyGC_generation0); \
_PyGC_generation0->_gc_prev = (uintptr_t)g; \
_PyGCHead_SET_NEXT(g, _PyRuntime.gc.generation0); \
_PyRuntime.gc.generation0->_gc_prev = (uintptr_t)g; \
} while (0);

/* Tell the GC to stop tracking this object.
*
* Internal note: This may be called while GC. So _PyGC_PREV_MASK_COLLECTING must
* be cleared. But _PyGC_PREV_MASK_FINALIZED bit is kept.
*
* The PyObject_GC_UnTrack() function is the public version of this macro.
*/
#define _PyObject_GC_UNTRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
Expand All @@ -347,25 +366,25 @@ extern PyGC_Head *_PyGC_generation0;
g->_gc_next = 0; \
g->_gc_prev &= _PyGC_PREV_MASK_FINALIZED; \
} while (0);

/* 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)))
#endif /* Py_LIMITED_API */
#endif /* defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN) */

#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size);
PyAPI_FUNC(PyObject *) _PyObject_GC_Calloc(size_t size);
#endif /* !Py_LIMITED_API */
PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *);
PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t);

/* Tell the GC to track this object.
*
* See also private _PyObject_GC_TRACK() macro. */
PyAPI_FUNC(void) PyObject_GC_Track(void *);

/* Tell the GC to stop tracking this object.
*
* See also private _PyObject_GC_UNTRACK() macro. */
PyAPI_FUNC(void) PyObject_GC_UnTrack(void *);

PyAPI_FUNC(void) PyObject_GC_Del(void *);

#define PyObject_GC_New(type, typeobj) \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The :c:func:`_PyObject_GC_TRACK` and :c:func:`_PyObject_GC_UNTRACK` macros
have been removed from the public C API.
2 changes: 1 addition & 1 deletion Modules/_queuemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ typedef struct {
static void
simplequeue_dealloc(simplequeueobject *self)
{
_PyObject_GC_UNTRACK(self);
PyObject_GC_UnTrack(self);
if (self->lock != NULL) {
/* Unlock the lock so it's safe to free it */
if (self->locked > 0)
Expand Down