Skip to content

Commit 6c58f38

Browse files
fangererqunaibit
authored andcommitted
[GR-52150] Python GC.
PullRequest: graalpython/3304
2 parents 6b345d3 + 9abace7 commit 6c58f38

File tree

65 files changed

+4687
-931
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+4687
-931
lines changed

graalpython/com.oracle.graal.python.cext/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ set(TARGET_LIBPYTHON "python-${LLVM_MODE}")
6060
# common variables and compile/link options (for all build targets)
6161
######################################################################
6262

63-
set(CFLAGS_WARNINGS -Wall -Werror -Wno-unused-function -Wno-unused-variable -Wno-unused-const-variable
63+
set(CFLAGS_WARNINGS -Wall -Werror,-Wunknown-warning-option -Wno-unused-function -Wno-unused-variable -Wno-unused-const-variable
6464
-Wno-tautological-constant-out-of-range-compare # fileutils.c: wchar_t > MAX_UNICODE is always false on Windows
6565
-Wno-unused-but-set-variable # sqlite.c: BOOL bRc
6666
-Wno-ignored-pragmas # sre.c: #pragma optimize("agtw", on)
67+
-Wno-discarded-qualifiers # longobject.c: *pend = str; (gcc only hence: '-Wunknown-warning-option')
6768
-Wno-int-to-pointer-cast -Wno-int-conversion -Wno-void-pointer-to-int-cast
6869
-Wno-incompatible-pointer-types-discards-qualifiers -Wno-pointer-type-mismatch
6970
-Wno-braced-scalar-init -Wno-deprecated-declarations)

graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,16 @@ struct _ts {
206206
/* The bottom-most frame on the stack. */
207207
_PyCFrame root_cframe;
208208

209-
/* GraalVM change: We add field 'small_ints' which roughly corresponds to
209+
/* GraalPy change: We add field 'small_ints' which roughly corresponds to
210210
field '_PyRuntimeState._Py_global_objects.small_ints'. We do so because
211211
we maintain the runtime state in Java objects and don't want to allocate
212212
the large structures and arrays where for us, most fields will be zero.
213213
*/
214214
PyObject **small_ints;
215+
216+
/* GraalPy change: We add field 'gc' which corresponds to field
217+
'&interp->gc'. */
218+
struct _gc_runtime_state *gc;
215219
};
216220

217221

graalpython/com.oracle.graal.python.cext/include/internal/pycore_gc.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1+
/* Copyright (c) 2022, 2024, Oracle and/or its affiliates.
22
* Copyright (C) 1996-2022 Python Software Foundation
33
*
44
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -27,8 +27,11 @@ typedef struct {
2727
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
2828
#define _PyGC_Head_UNUSED PyGC_Head
2929

30+
// GraalPy change
31+
#define _PyGCHead_UNTAG(PTR) ((PyGC_Head *)(((uintptr_t) (PTR)) & ~HANDLE_BASE))
32+
3033
/* True if the object is currently tracked by the GC. */
31-
#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0)
34+
#define _PyObject_GC_IS_TRACKED(o) (_PyGCHead_UNTAG(_Py_AS_GC(o))->_gc_next != 0)
3235

3336
/* True if the object may be tracked by the GC in the future, or already is.
3437
This can be useful to implement some optimizations. */
@@ -48,21 +51,21 @@ typedef struct {
4851

4952
// Lowest bit of _gc_next is used for flags only in GC.
5053
// But it is always 0 for normal code.
51-
#define _PyGCHead_NEXT(g) ((PyGC_Head*)(g)->_gc_next)
52-
#define _PyGCHead_SET_NEXT(g, p) _Py_RVALUE((g)->_gc_next = (uintptr_t)(p))
54+
#define _PyGCHead_NEXT(g) ((PyGC_Head*)_PyGCHead_UNTAG(g)->_gc_next)
55+
#define _PyGCHead_SET_NEXT(g, p) _Py_RVALUE(_PyGCHead_UNTAG(g)->_gc_next = (uintptr_t)(p))
5356

5457
// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.
55-
#define _PyGCHead_PREV(g) ((PyGC_Head*)((g)->_gc_prev & _PyGC_PREV_MASK))
58+
#define _PyGCHead_PREV(g) ((PyGC_Head*)(_PyGCHead_UNTAG(g)->_gc_prev & _PyGC_PREV_MASK))
5659
#define _PyGCHead_SET_PREV(g, p) do { \
5760
assert(((uintptr_t)p & ~_PyGC_PREV_MASK) == 0); \
58-
(g)->_gc_prev = ((g)->_gc_prev & ~_PyGC_PREV_MASK) \
61+
_PyGCHead_UNTAG(g)->_gc_prev = (_PyGCHead_UNTAG(g)->_gc_prev & ~_PyGC_PREV_MASK) \
5962
| ((uintptr_t)(p)); \
6063
} while (0)
6164

6265
#define _PyGCHead_FINALIZED(g) \
63-
(((g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0)
66+
((_PyGCHead_UNTAG(g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0)
6467
#define _PyGCHead_SET_FINALIZED(g) \
65-
_Py_RVALUE((g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED)
68+
_Py_RVALUE(_PyGCHead_UNTAG(g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED)
6669

6770
#define _PyGC_FINALIZED(o) \
6871
_PyGCHead_FINALIZED(_Py_AS_GC(o))

graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ extern "C" {
1414
#endif
1515

1616
#include <stdbool.h>
17-
// #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
18-
// #include "pycore_interp.h" // PyInterpreterState.gc
19-
// #include "pycore_pystate.h" // _PyInterpreterState_GET()
20-
// #include "pycore_runtime.h" // _PyRuntime
17+
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
18+
#if 0 // GraalPy change
19+
#include "pycore_interp.h" // PyInterpreterState.gc
20+
#endif
21+
#include "pycore_pystate.h" // _PyInterpreterState_GET()
22+
#if 0 // GraalPy change
23+
#include "pycore_runtime.h" // _PyRuntime
24+
#endif
2125

2226
#define _PyObject_IMMORTAL_INIT(type) \
2327
{ \
@@ -134,8 +138,28 @@ static inline void _PyObject_GC_TRACK(
134138
#endif
135139
PyObject *op)
136140
{
137-
// GraalPy change
138-
PyObject_GC_Track(op);
141+
_PyObject_ASSERT_FROM(op, !_PyObject_GC_IS_TRACKED(op),
142+
"object already tracked by the garbage collector",
143+
filename, lineno, __func__);
144+
145+
PyGC_Head *gc = _Py_AS_GC(op);
146+
_PyObject_ASSERT_FROM(op,
147+
(gc->_gc_prev & _PyGC_PREV_MASK_COLLECTING) == 0,
148+
"object is in generation which is garbage collected",
149+
filename, lineno, __func__);
150+
151+
#if 0 // GraalPy change
152+
PyInterpreterState *interp = _PyInterpreterState_GET();
153+
PyGC_Head *generation0 = interp->gc.generation0;
154+
#else // GraalPy change
155+
PyThreadState *tstate = _PyThreadState_GET();
156+
PyGC_Head *generation0 = tstate->gc->generation0;
157+
#endif // GraalPy change
158+
PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
159+
_PyGCHead_SET_NEXT(last, gc);
160+
_PyGCHead_SET_PREV(gc, last);
161+
_PyGCHead_SET_NEXT(gc, generation0);
162+
generation0->_gc_prev = (uintptr_t)gc;
139163
}
140164

141165
/* Tell the GC to stop tracking this object.
@@ -155,13 +179,26 @@ static inline void _PyObject_GC_UNTRACK(
155179
#endif
156180
PyObject *op)
157181
{
158-
// GraalPy change
159-
PyObject_GC_UnTrack(op);
182+
_PyObject_ASSERT_FROM(op, _PyObject_GC_IS_TRACKED(op),
183+
"object not tracked by the garbage collector",
184+
filename, lineno, __func__);
185+
186+
PyGC_Head *gc = _Py_AS_GC(op);
187+
PyGC_Head *prev = _PyGCHead_PREV(gc);
188+
PyGC_Head *next = _PyGCHead_NEXT(gc);
189+
_PyGCHead_SET_NEXT(prev, next);
190+
_PyGCHead_SET_PREV(next, prev);
191+
_PyGCHead_UNTAG(gc)->_gc_next = 0;
192+
_PyGCHead_UNTAG(gc)->_gc_prev &= _PyGC_PREV_MASK_FINALIZED;
160193
}
161194

162195
// Macros to accept any type for the parameter, and to automatically pass
163196
// the filename and the filename (if NDEBUG is not defined) where the macro
164197
// is called.
198+
#ifdef GRAALVM_PYTHON_LLVM_MANAGED
199+
# define _PyObject_GC_TRACK(op)
200+
# define _PyObject_GC_UNTRACK(op)
201+
#else
165202
#ifdef NDEBUG
166203
# define _PyObject_GC_TRACK(op) \
167204
_PyObject_GC_TRACK(_PyObject_CAST(op))
@@ -173,6 +210,7 @@ static inline void _PyObject_GC_UNTRACK(
173210
# define _PyObject_GC_UNTRACK(op) \
174211
_PyObject_GC_UNTRACK(__FILE__, __LINE__, _PyObject_CAST(op))
175212
#endif
213+
#endif
176214

177215
#ifdef Py_REF_DEBUG
178216
extern void _PyDebug_PrintTotalRefs(void);
@@ -197,7 +235,12 @@ _PyObject_IS_GC(PyObject *obj)
197235
{
198236
return (PyType_IS_GC(Py_TYPE(obj))
199237
&& (Py_TYPE(obj)->tp_is_gc == NULL
200-
|| Py_TYPE(obj)->tp_is_gc(obj)));
238+
|| Py_TYPE(obj)->tp_is_gc(obj))
239+
/* GraalPy change: our built-in types do not yet consistently
240+
declare the HAVE_GC flag. So, also check if 'tp_traverse'
241+
is there. */
242+
&& (!points_to_py_handle_space(obj)
243+
|| Py_TYPE(obj)->tp_traverse != NULL));
201244
}
202245

203246
// Fast inlined version of PyType_IS_GC()
@@ -206,12 +249,15 @@ _PyObject_IS_GC(PyObject *obj)
206249
static inline size_t
207250
_PyType_PreHeaderSize(PyTypeObject *tp)
208251
{
209-
// GraalPy change: remove CPython's GC header; also we put only one pointer for dict, we don't store it inlined
210-
return _PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT) * sizeof(PyObject *);
252+
// GraalPy change: we put only one pointer for dict, we don't store it inlined
253+
return _PyType_IS_GC(tp) * sizeof(PyGC_Head) +
254+
_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT) * sizeof(PyObject *);
211255
}
212256

213257
void _PyObject_GC_Link(PyObject *op);
214258

259+
void _GraalPyObject_GC_NotifyOwnershipTransfer(PyObject *op);
260+
215261
// Usage: assert(_Py_CheckSlotResult(obj, "__getitem__", result != NULL));
216262
extern int _Py_CheckSlotResult(
217263
PyObject *obj,
@@ -245,8 +291,7 @@ static inline PyDictValues **_PyObject_ValuesPointer(PyObject *obj)
245291
static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj)
246292
{
247293
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
248-
// GraalPy change: ours is at a different offset
249-
return ((PyObject **)obj)-1;
294+
return ((PyObject **)obj)-3;
250295
}
251296

252297
#define MANAGED_DICT_OFFSET (((int)sizeof(PyObject *))*-3)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
2+
* Copyright (C) 1996-2023 Python Software Foundation
3+
*
4+
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
*/
6+
7+
/* Static DTrace probes interface */
8+
9+
#ifndef Py_DTRACE_H
10+
#define Py_DTRACE_H
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
#ifdef WITH_DTRACE
16+
17+
#include "pydtrace_probes.h"
18+
19+
/* pydtrace_probes.h, on systems with DTrace, is auto-generated to include
20+
`PyDTrace_{PROBE}` and `PyDTrace_{PROBE}_ENABLED()` macros for every probe
21+
defined in pydtrace_provider.d.
22+
23+
Calling these functions must be guarded by a `PyDTrace_{PROBE}_ENABLED()`
24+
check to minimize performance impact when probing is off. For example:
25+
26+
if (PyDTrace_FUNCTION_ENTRY_ENABLED())
27+
PyDTrace_FUNCTION_ENTRY(f);
28+
*/
29+
30+
#else
31+
32+
/* Without DTrace, compile to nothing. */
33+
34+
static inline void PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {}
35+
static inline void PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {}
36+
static inline void PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {}
37+
static inline void PyDTrace_GC_START(int arg0) {}
38+
static inline void PyDTrace_GC_DONE(Py_ssize_t arg0) {}
39+
static inline void PyDTrace_INSTANCE_NEW_START(int arg0) {}
40+
static inline void PyDTrace_INSTANCE_NEW_DONE(int arg0) {}
41+
static inline void PyDTrace_INSTANCE_DELETE_START(int arg0) {}
42+
static inline void PyDTrace_INSTANCE_DELETE_DONE(int arg0) {}
43+
static inline void PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {}
44+
static inline void PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {}
45+
static inline void PyDTrace_AUDIT(const char *arg0, void *arg1) {}
46+
47+
static inline int PyDTrace_LINE_ENABLED(void) { return 0; }
48+
static inline int PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; }
49+
static inline int PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; }
50+
static inline int PyDTrace_GC_START_ENABLED(void) { return 0; }
51+
static inline int PyDTrace_GC_DONE_ENABLED(void) { return 0; }
52+
static inline int PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; }
53+
static inline int PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; }
54+
static inline int PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; }
55+
static inline int PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; }
56+
static inline int PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; }
57+
static inline int PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; }
58+
static inline int PyDTrace_AUDIT_ENABLED(void) { return 0; }
59+
60+
#endif /* !WITH_DTRACE */
61+
62+
#ifdef __cplusplus
63+
}
64+
#endif
65+
#endif /* !Py_DTRACE_H */

0 commit comments

Comments
 (0)