summaryrefslogtreecommitdiffstats
path: root/Include
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-02-05 18:28:51 (GMT)
committerGitHub <noreply@github.com>2024-02-05 18:28:51 (GMT)
commit36518e69d74607e5f094ce55286188e4545a947d (patch)
treeb031b3cb351b68e5bee0ad4bf346fd958b2b9307 /Include
parentb4ba0f73d6eef3da321bb96aafd09dfbc572e95d (diff)
downloadcpython-36518e69d74607e5f094ce55286188e4545a947d.zip
cpython-36518e69d74607e5f094ce55286188e4545a947d.tar.gz
cpython-36518e69d74607e5f094ce55286188e4545a947d.tar.bz2
GH-108362: Incremental GC implementation (GH-108038)
Diffstat (limited to 'Include')
-rw-r--r--Include/internal/pycore_gc.h42
-rw-r--r--Include/internal/pycore_object.h17
-rw-r--r--Include/internal/pycore_runtime_init.h8
3 files changed, 34 insertions, 33 deletions
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index ca1d9fd..d2f5c69 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -71,11 +71,15 @@ static inline int _PyObject_GC_MAY_BE_TRACKED(PyObject *obj) {
/* Bit flags for _gc_prev */
/* Bit 0 is set when tp_finalize is called */
-#define _PyGC_PREV_MASK_FINALIZED (1)
+#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)
+#define _PyGC_PREV_MASK_COLLECTING 2
+
+/* Bit 0 is set if the object belongs to old space 1 */
+#define _PyGC_NEXT_MASK_OLD_SPACE_1 1
+
/* The (N-2) most significant bits contain the real address. */
-#define _PyGC_PREV_SHIFT (2)
+#define _PyGC_PREV_SHIFT 2
#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
/* set for debugging information */
@@ -101,11 +105,13 @@ typedef enum {
// Lowest bit of _gc_next is used for flags only in GC.
// But it is always 0 for normal code.
static inline PyGC_Head* _PyGCHead_NEXT(PyGC_Head *gc) {
- uintptr_t next = gc->_gc_next;
+ uintptr_t next = gc->_gc_next & _PyGC_PREV_MASK;
return (PyGC_Head*)next;
}
static inline void _PyGCHead_SET_NEXT(PyGC_Head *gc, PyGC_Head *next) {
- gc->_gc_next = (uintptr_t)next;
+ uintptr_t unext = (uintptr_t)next;
+ assert((unext & ~_PyGC_PREV_MASK) == 0);
+ gc->_gc_next = (gc->_gc_next & ~_PyGC_PREV_MASK) | unext;
}
// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.
@@ -113,6 +119,7 @@ static inline PyGC_Head* _PyGCHead_PREV(PyGC_Head *gc) {
uintptr_t prev = (gc->_gc_prev & _PyGC_PREV_MASK);
return (PyGC_Head*)prev;
}
+
static inline void _PyGCHead_SET_PREV(PyGC_Head *gc, PyGC_Head *prev) {
uintptr_t uprev = (uintptr_t)prev;
assert((uprev & ~_PyGC_PREV_MASK) == 0);
@@ -198,6 +205,13 @@ struct gc_generation {
generations */
};
+struct gc_collection_stats {
+ /* number of collected objects */
+ Py_ssize_t collected;
+ /* total number of uncollectable objects (put into gc.garbage) */
+ Py_ssize_t uncollectable;
+};
+
/* Running stats per generation */
struct gc_generation_stats {
/* total number of collections */
@@ -219,8 +233,8 @@ struct _gc_runtime_state {
int enabled;
int debug;
/* linked lists of container objects */
- struct gc_generation generations[NUM_GENERATIONS];
- PyGC_Head *generation0;
+ struct gc_generation young;
+ struct gc_generation old[2];
/* a permanent generation which won't be collected */
struct gc_generation permanent_generation;
struct gc_generation_stats generation_stats[NUM_GENERATIONS];
@@ -233,22 +247,20 @@ struct _gc_runtime_state {
/* This is the number of objects that survived the last full
collection. It approximates the number of long lived objects
tracked by the GC.
-
(by "full collection", we mean a collection of the oldest
generation). */
Py_ssize_t long_lived_total;
- /* This is the number of objects that survived all "non-full"
- collections, and are awaiting to undergo a full collection for
- the first time. */
- Py_ssize_t long_lived_pending;
+
+ Py_ssize_t work_to_do;
+ /* Which of the old spaces is the visited space */
+ int visited_space;
};
extern void _PyGC_InitState(struct _gc_runtime_state *);
-extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation,
- _PyGC_Reason reason);
-extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate);
+extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason);
+extern void _PyGC_CollectNoFail(PyThreadState *tstate);
/* Freeze objects tracked by the GC and ignore them in future collections. */
extern void _PyGC_Freeze(PyInterpreterState *interp);
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 34a83ea..efa712c 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -125,19 +125,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
}
#define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n)
-static inline void _Py_SetImmortal(PyObject *op)
-{
- if (op) {
-#ifdef Py_GIL_DISABLED
- op->ob_tid = _Py_UNOWNED_TID;
- op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
- op->ob_ref_shared = 0;
-#else
- op->ob_refcnt = _Py_IMMORTAL_REFCNT;
-#endif
- }
-}
-#define _Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))
+extern void _Py_SetImmortal(PyObject *op);
// Makes an immortal object mortal again with the specified refcnt. Should only
// be used during runtime finalization.
@@ -325,11 +313,12 @@ static inline void _PyObject_GC_TRACK(
filename, lineno, __func__);
PyInterpreterState *interp = _PyInterpreterState_GET();
- PyGC_Head *generation0 = interp->gc.generation0;
+ PyGC_Head *generation0 = &interp->gc.young.head;
PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
_PyGCHead_SET_NEXT(last, gc);
_PyGCHead_SET_PREV(gc, last);
_PyGCHead_SET_NEXT(gc, generation0);
+ assert((gc->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1) == 0);
generation0->_gc_prev = (uintptr_t)gc;
#endif
}
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index 0a5c92b..4370ad0 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -160,12 +160,12 @@ extern PyTypeObject _PyExc_MemoryError;
}, \
.gc = { \
.enabled = 1, \
- .generations = { \
- /* .head is set in _PyGC_InitState(). */ \
- { .threshold = 700, }, \
- { .threshold = 10, }, \
+ .young = { .threshold = 2000, }, \
+ .old = { \
{ .threshold = 10, }, \
+ { .threshold = 0, }, \
}, \
+ .work_to_do = -5000, \
}, \
.object_state = _py_object_state_INIT(INTERP), \
.dtoa = _dtoa_state_INIT(&(INTERP)), \