Kaydet (Commit) ea405639 authored tarafından Tim Peters's avatar Tim Peters

Reserved another gc_refs value for untracked objects. Every live gc

object should now have a well-defined gc_refs value, with clear transitions
among gc_refs states.  As a result, none of the visit_XYZ traversal
callbacks need to check IS_TRACKED() anymore, and those tests were removed.
(They were already looking for objects with specific gc_refs states, and
the gc_refs state of an untracked object can no longer match any other
gc_refs state by accident.)
Added more asserts.
I expect that the gc_next == NULL indicator for an untracked object is
now redundant and can also be removed, but I ran out of time for this.
üst 7c75bf20
...@@ -262,12 +262,18 @@ extern PyGC_Head *_PyGC_generation0; ...@@ -262,12 +262,18 @@ extern PyGC_Head *_PyGC_generation0;
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) #define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
#define _PyGC_REFS_UNTRACKED (-2)
#define _PyGC_REFS_REACHABLE (-3)
#define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4)
/* Tell the GC to track this object. NB: While the object is tracked the /* Tell the GC to track this object. NB: While the object is tracked the
* collector it must be safe to call the ob_traverse method. */ * collector it must be safe to call the ob_traverse method. */
#define _PyObject_GC_TRACK(o) do { \ #define _PyObject_GC_TRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \ PyGC_Head *g = _Py_AS_GC(o); \
if (g->gc.gc_next != NULL) \ if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED) \
Py_FatalError("GC object already in linked list"); \ Py_FatalError("GC object already tracked"); \
assert(g->gc.gc_refs == _PyGC_REFS_UNTRACKED); \
g->gc.gc_refs = _PyGC_REFS_REACHABLE; \
g->gc.gc_next = _PyGC_generation0; \ g->gc.gc_next = _PyGC_generation0; \
g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \ g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
g->gc.gc_prev->gc.gc_next = g; \ g->gc.gc_prev->gc.gc_next = g; \
...@@ -277,6 +283,8 @@ extern PyGC_Head *_PyGC_generation0; ...@@ -277,6 +283,8 @@ extern PyGC_Head *_PyGC_generation0;
/* Tell the GC to stop tracking this object. */ /* Tell the GC to stop tracking this object. */
#define _PyObject_GC_UNTRACK(o) do { \ #define _PyObject_GC_UNTRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \ PyGC_Head *g = _Py_AS_GC(o); \
assert(g->gc.gc_refs != _PyGC_REFS_UNTRACKED); \
g->gc.gc_refs = _PyGC_REFS_UNTRACKED; \
g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \ g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \
g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \ g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \
g->gc.gc_next = NULL; \ g->gc.gc_next = NULL; \
......
...@@ -82,8 +82,9 @@ static int debug; ...@@ -82,8 +82,9 @@ static int debug;
*/ */
/* Special gc_refs values. */ /* Special gc_refs values. */
#define GC_REACHABLE -123 #define GC_UNTRACKED _PyGC_REFS_UNTRACKED
#define GC_TENTATIVELY_UNREACHABLE -42 #define GC_REACHABLE _PyGC_REFS_REACHABLE
#define GC_TENTATIVELY_UNREACHABLE _PyGC_REFS_TENTATIVELY_UNREACHABLE
#define IS_REACHABLE(o) ((AS_GC(o))->gc.gc_refs == GC_REACHABLE) #define IS_REACHABLE(o) ((AS_GC(o))->gc.gc_refs == GC_REACHABLE)
#define IS_TENTATIVELY_UNREACHABLE(o) ( \ #define IS_TENTATIVELY_UNREACHABLE(o) ( \
...@@ -179,8 +180,10 @@ static void ...@@ -179,8 +180,10 @@ static void
update_refs(PyGC_Head *containers) update_refs(PyGC_Head *containers)
{ {
PyGC_Head *gc = containers->gc.gc_next; PyGC_Head *gc = containers->gc.gc_next;
for (; gc != containers; gc = gc->gc.gc_next) for (; gc != containers; gc = gc->gc.gc_next) {
assert(gc->gc.gc_refs == GC_REACHABLE);
gc->gc.gc_refs = FROM_GC(gc)->ob_refcnt; gc->gc.gc_refs = FROM_GC(gc)->ob_refcnt;
}
} }
/* A traversal callback for subtract_refs. */ /* A traversal callback for subtract_refs. */
...@@ -222,7 +225,7 @@ subtract_refs(PyGC_Head *containers) ...@@ -222,7 +225,7 @@ subtract_refs(PyGC_Head *containers)
static int static int
visit_reachable(PyObject *op, PyGC_Head *reachable) visit_reachable(PyObject *op, PyGC_Head *reachable)
{ {
if (PyObject_IS_GC(op) && IS_TRACKED(op)) { if (PyObject_IS_GC(op)) {
PyGC_Head *gc = AS_GC(op); PyGC_Head *gc = AS_GC(op);
const int gc_refs = gc->gc.gc_refs; const int gc_refs = gc->gc.gc_refs;
...@@ -250,8 +253,14 @@ visit_reachable(PyObject *op, PyGC_Head *reachable) ...@@ -250,8 +253,14 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
* list, and move_unreachable will eventually get to it. * list, and move_unreachable will eventually get to it.
* If gc_refs == GC_REACHABLE, it's either in some other * If gc_refs == GC_REACHABLE, it's either in some other
* generation so we don't care about it, or move_unreachable * generation so we don't care about it, or move_unreachable
* already dealt with it. * already deat with it.
* If gc_refs == GC_UNTRACKED, it must be ignored.
*/ */
else {
assert(gc_refs > 0
|| gc_refs == GC_REACHABLE
|| gc_refs == GC_UNTRACKED);
}
} }
return 0; return 0;
} }
...@@ -352,7 +361,7 @@ static int ...@@ -352,7 +361,7 @@ static int
visit_move(PyObject *op, PyGC_Head *tolist) visit_move(PyObject *op, PyGC_Head *tolist)
{ {
if (PyObject_IS_GC(op)) { if (PyObject_IS_GC(op)) {
if (IS_TRACKED(op) && IS_TENTATIVELY_UNREACHABLE(op)) { if (IS_TENTATIVELY_UNREACHABLE(op)) {
PyGC_Head *gc = AS_GC(op); PyGC_Head *gc = AS_GC(op);
gc_list_remove(gc); gc_list_remove(gc);
gc_list_append(gc, tolist); gc_list_append(gc, tolist);
...@@ -966,6 +975,7 @@ _PyObject_GC_Malloc(size_t basicsize) ...@@ -966,6 +975,7 @@ _PyObject_GC_Malloc(size_t basicsize)
if (g == NULL) if (g == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
g->gc.gc_next = NULL; g->gc.gc_next = NULL;
g->gc.gc_refs = GC_UNTRACKED;
generations[0].count++; /* number of allocated GC objects */ generations[0].count++; /* number of allocated GC objects */
if (generations[0].count > generations[0].threshold && if (generations[0].count > generations[0].threshold &&
enabled && enabled &&
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment