Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fc13d9

Browse files
committed
Finished transitioning to using gc_refs to track gc objects' states.
This was mostly a matter of adding comments and light code rearrangement. Upon untracking, gc_next is still set to NULL. It's a cheap way to provoke memory faults if calling code is insane. It's also used in some way by the trashcan mechanism.
1 parent 8e8dc41 commit 6fc13d9

2 files changed

Lines changed: 71 additions & 42 deletions

File tree

Include/objimpl.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ extern DL_IMPORT(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, int);
251251
/* GC information is stored BEFORE the object structure. */
252252
typedef union _gc_head {
253253
struct {
254-
union _gc_head *gc_next; /* not NULL if object is tracked */
254+
union _gc_head *gc_next;
255255
union _gc_head *gc_prev;
256256
int gc_refs;
257257
} gc;
@@ -272,15 +272,17 @@ extern PyGC_Head *_PyGC_generation0;
272272
PyGC_Head *g = _Py_AS_GC(o); \
273273
if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED) \
274274
Py_FatalError("GC object already tracked"); \
275-
assert(g->gc.gc_refs == _PyGC_REFS_UNTRACKED); \
276275
g->gc.gc_refs = _PyGC_REFS_REACHABLE; \
277276
g->gc.gc_next = _PyGC_generation0; \
278277
g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
279278
g->gc.gc_prev->gc.gc_next = g; \
280279
_PyGC_generation0->gc.gc_prev = g; \
281280
} while (0);
282281

283-
/* Tell the GC to stop tracking this object. */
282+
/* Tell the GC to stop tracking this object.
283+
* gc_next doesn't need to be set to NULL, but doing so is a good
284+
* way to provoke memory errors if calling code is confused.
285+
*/
284286
#define _PyObject_GC_UNTRACK(o) do { \
285287
PyGC_Head *g = _Py_AS_GC(o); \
286288
assert(g->gc.gc_refs != _PyGC_REFS_UNTRACKED); \

Modules/gcmodule.c

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@
2828
/* Get the object given the GC head */
2929
#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
3030

31-
/* True if an object is tracked by the GC */
32-
#define IS_TRACKED(o) ((AS_GC(o))->gc.gc_next != NULL)
33-
3431
/*** Global GC state ***/
3532

3633
struct gc_generation {
@@ -58,6 +55,12 @@ static int enabled = 1; /* automatic collection enabled? */
5855
/* true if we are currently running the collector */
5956
static int collecting;
6057

58+
/* list of uncollectable objects */
59+
static PyObject *garbage;
60+
61+
/* Python string to use if unhandled exception occurs */
62+
static PyObject *gc_str;
63+
6164
/* set for debugging information */
6265
#define DEBUG_STATS (1<<0) /* print collection statistics */
6366
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
@@ -72,30 +75,54 @@ static int collecting;
7275
DEBUG_SAVEALL
7376
static int debug;
7477

75-
/* When a collection begins, gc_refs is set to ob_refcnt for, and only for,
76-
* the objects in the generation being collected, called the "young"
77-
* generation at that point. As collection proceeds, the gc_refs members
78-
* of young objects are set to GC_REACHABLE when it becomes known that they're
79-
* uncollectable, and to GC_TENTATIVELY_UNREACHABLE when the evidence
80-
* suggests they are collectable (this can't be known for certain until all
81-
* of the young generation is scanned).
82-
*/
83-
84-
/* Special gc_refs values. */
78+
/*--------------------------------------------------------------------------
79+
gc_refs values.
80+
81+
Between collections, every gc'ed object has one of two gc_refs values:
82+
83+
GC_UNTRACKED
84+
The initial state; objects returned by PyObject_GC_Malloc are in this
85+
state. The object doesn't live in any generation list, and its
86+
tp_traverse slot must not be called.
87+
88+
GC_REACHABLE
89+
The object lives in some generation list, and its tp_traverse is safe to
90+
call. An object transitions to GC_REACHABLE when PyObject_GC_Track
91+
is called.
92+
93+
During a collection, gc_refs can temporarily take on other states:
94+
95+
>= 0
96+
At the start of a collection, update_refs() copies the true refcount
97+
to gc_refs, for each object in the generation being collected.
98+
subtract_refs() then adjusts gc_refs so that it equals the number of
99+
times an object is referenced directly from outside the generation
100+
being collected.
101+
gc_refs reamins >= 0 throughout these steps.
102+
103+
GC_TENTATIVELY_UNREACHABLE
104+
move_unreachable() then moves objects not reachable (whether directly or
105+
indirectly) from outside the generation into an "unreachable" set.
106+
Objects that are found to be reachable have gc_refs set to GC_REACHABLE
107+
again. Objects that are found to be unreachable have gc_refs set to
108+
GC_TENTATIVELY_UNREACHABLE. It's "tentatively" because the pass doing
109+
this can't be sure until it ends, and GC_TENTATIVELY_UNREACHABLE may
110+
transition back to GC_REACHABLE.
111+
112+
Only objects with GC_TENTATIVELY_UNREACHABLE still set are candidates
113+
for collection. If it's decided not to collect such an object (e.g.,
114+
it has a __del__ method), its gc_refs is restored to GC_REACHABLE again.
115+
----------------------------------------------------------------------------
116+
*/
85117
#define GC_UNTRACKED _PyGC_REFS_UNTRACKED
86118
#define GC_REACHABLE _PyGC_REFS_REACHABLE
87119
#define GC_TENTATIVELY_UNREACHABLE _PyGC_REFS_TENTATIVELY_UNREACHABLE
88120

121+
#define IS_TRACKED(o) ((AS_GC(o))->gc.gc_refs != GC_UNTRACKED)
89122
#define IS_REACHABLE(o) ((AS_GC(o))->gc.gc_refs == GC_REACHABLE)
90123
#define IS_TENTATIVELY_UNREACHABLE(o) ( \
91124
(AS_GC(o))->gc.gc_refs == GC_TENTATIVELY_UNREACHABLE)
92125

93-
/* list of uncollectable objects */
94-
static PyObject *garbage;
95-
96-
/* Python string to use if unhandled exception occurs */
97-
static PyObject *gc_str;
98-
99126
/*** list functions ***/
100127

101128
static void
@@ -253,7 +280,7 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
253280
* list, and move_unreachable will eventually get to it.
254281
* If gc_refs == GC_REACHABLE, it's either in some other
255282
* generation so we don't care about it, or move_unreachable
256-
* already deat with it.
283+
* already dealt with it.
257284
* If gc_refs == GC_UNTRACKED, it must be ignored.
258285
*/
259286
else {
@@ -290,7 +317,25 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
290317
while (gc != young) {
291318
PyGC_Head *next;
292319

293-
if (gc->gc.gc_refs == 0) {
320+
if (gc->gc.gc_refs) {
321+
/* gc is definitely reachable from outside the
322+
* original 'young'. Mark it as such, and traverse
323+
* its pointers to find any other objects that may
324+
* be directly reachable from it. Note that the
325+
* call to tp_traverse may append objects to young,
326+
* so we have to wait until it returns to determine
327+
* the next object to visit.
328+
*/
329+
PyObject *op = FROM_GC(gc);
330+
traverseproc traverse = op->ob_type->tp_traverse;
331+
assert(gc->gc.gc_refs > 0);
332+
gc->gc.gc_refs = GC_REACHABLE;
333+
(void) traverse(op,
334+
(visitproc)visit_reachable,
335+
(void *)young);
336+
next = gc->gc.gc_next;
337+
}
338+
else {
294339
/* This *may* be unreachable. To make progress,
295340
* assume it is. gc isn't directly reachable from
296341
* any object we've already traversed, but may be
@@ -303,23 +348,6 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
303348
gc_list_append(gc, unreachable);
304349
gc->gc.gc_refs = GC_TENTATIVELY_UNREACHABLE;
305350
}
306-
else {
307-
/* gc is definitely reachable from outside the
308-
* original 'young'. Mark it as such, and traverse
309-
* its pointers to find any other objects that may
310-
* be directly reachable from it. Note that the
311-
* call to tp_traverse may append objects to young,
312-
* so we have to wait until it returns to determine
313-
* the next object to visit.
314-
*/
315-
PyObject *op = FROM_GC(gc);
316-
traverseproc traverse = op->ob_type->tp_traverse;
317-
gc->gc.gc_refs = GC_REACHABLE;
318-
(void) traverse(op,
319-
(visitproc)visit_reachable,
320-
(void *)young);
321-
next = gc->gc.gc_next;
322-
}
323351
gc = next;
324352
}
325353
}
@@ -974,7 +1002,6 @@ _PyObject_GC_Malloc(size_t basicsize)
9741002
PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize);
9751003
if (g == NULL)
9761004
return PyErr_NoMemory();
977-
g->gc.gc_next = NULL;
9781005
g->gc.gc_refs = GC_UNTRACKED;
9791006
generations[0].count++; /* number of allocated GC objects */
9801007
if (generations[0].count > generations[0].threshold &&

0 commit comments

Comments
 (0)