Skip to content

Commit 78e5fe4

Browse files
committed
Copy and pasta the needed bits
1 parent 9c4c427 commit 78e5fe4

1 file changed

Lines changed: 234 additions & 6 deletions

File tree

Objects/tracingregionobject.c

Lines changed: 234 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,228 @@
11
#include "Python.h"
2+
#include "pycore_interp.h"
23
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
34
#include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats()
5+
#include "pycore_descrobject.h"
6+
7+
// #define REGION_TRACING
8+
9+
#ifdef REGION_TRACING
10+
#define if_trace(...) __VA_ARGS__
11+
#define trace_arg(arg) , (Py_uintptr_t)(arg)
12+
#define trace(msg, region, ...) \
13+
do { \
14+
printf(msg "\n", (Py_region_t)(region) __VA_OPT__(,) __VA_ARGS__); \
15+
} while(0)
16+
#define trace_lrc(...) trace(__VA_ARGS__)
17+
#else
18+
#define if_trace(...)
19+
#define trace_arg(...)
20+
#define trace(...)
21+
#define trace_lrc(...)
22+
#endif
23+
24+
/* Macro that jumps to error, if the expression `x` does not succeed. */
25+
#define SUCCEEDS(x) do { int r = (x); if (r != 0) goto error; } while (0)
26+
27+
// ###################################################################
28+
// Copied from gc.c
29+
// ###################################################################
30+
31+
#ifndef Py_GIL_DISABLED
32+
#define GC_NEXT _PyGCHead_NEXT
33+
#define GC_PREV _PyGCHead_PREV
34+
35+
static inline void
36+
gc_set_old_space(PyGC_Head *g, int space)
37+
{
38+
assert(space == 0 || space == _PyGC_NEXT_MASK_OLD_SPACE_1);
39+
g->_gc_next &= ~_PyGC_NEXT_MASK_OLD_SPACE_1;
40+
g->_gc_next |= space;
41+
}
42+
43+
static inline void
44+
gc_list_init(PyGC_Head *list)
45+
{
46+
// List header must not have flags.
47+
// We can assign pointer by simple cast.
48+
list->_gc_prev = (uintptr_t)list;
49+
list->_gc_next = (uintptr_t)list;
50+
}
51+
52+
static void
53+
gc_list_move(PyGC_Head *node, PyGC_Head *list)
54+
{
55+
/* Unlink from current list. */
56+
PyGC_Head *from_prev = GC_PREV(node);
57+
PyGC_Head *from_next = GC_NEXT(node);
58+
_PyGCHead_SET_NEXT(from_prev, from_next);
59+
_PyGCHead_SET_PREV(from_next, from_prev);
60+
61+
/* Relink at end of new list. */
62+
// list must not have flags. So we can skip macros.
63+
PyGC_Head *to_prev = (PyGC_Head*)list->_gc_prev;
64+
_PyGCHead_SET_PREV(node, to_prev);
65+
_PyGCHead_SET_NEXT(to_prev, node);
66+
list->_gc_prev = (uintptr_t)node;
67+
_PyGCHead_SET_NEXT(node, list);
68+
}
69+
70+
static inline int
71+
gc_list_is_empty(PyGC_Head *list)
72+
{
73+
return (list->_gc_next == (uintptr_t)list);
74+
}
75+
76+
static void
77+
gc_list_merge(PyGC_Head *from, PyGC_Head *to)
78+
{
79+
assert(from != to);
80+
if (!gc_list_is_empty(from)) {
81+
PyGC_Head *to_tail = GC_PREV(to);
82+
PyGC_Head *from_head = GC_NEXT(from);
83+
PyGC_Head *from_tail = GC_PREV(from);
84+
assert(from_head != from);
85+
assert(from_tail != from);
86+
87+
_PyGCHead_SET_NEXT(to_tail, from_head);
88+
_PyGCHead_SET_PREV(from_head, to_tail);
89+
90+
_PyGCHead_SET_NEXT(from_tail, to);
91+
_PyGCHead_SET_PREV(to, from_tail);
92+
}
93+
gc_list_init(from);
94+
}
95+
96+
static struct _gc_runtime_state*
97+
get_gc_state(void)
98+
{
99+
PyInterpreterState *interp = _PyInterpreterState_GET();
100+
return &interp->gc;
101+
}
102+
103+
static inline void
104+
gc_clear_collecting(PyGC_Head *g)
105+
{
106+
g->_gc_prev &= ~_PyGC_PREV_MASK_COLLECTING;
107+
}
108+
109+
#elif // Py_GIL_DISABLED
110+
#error "We need GIL"
111+
#endif
112+
113+
// ###################################################################
114+
// Copied from regions-main
115+
// ###################################################################
116+
117+
typedef enum {
118+
Py_MOVABLE_YES = 0,
119+
Py_MOVABLE_NO = 1,
120+
Py_MOVABLE_FREEZE = 2,
121+
} movable_status;
122+
123+
movable_status get_movable_status(PyObject *obj) {
124+
// FIXME(regions): xFrednet: Currently it's not possible to set
125+
// the movability per object. This instead returns the default
126+
// movability for objects. Note that some shallow immutable objects
127+
// will not return freeze as their movability.
128+
129+
// Immortal object have no real RC, this makes it infeasible to have them
130+
// in a region and dynamically track their ownership. Immortal objects are
131+
// intended to be immutable in Python, so it should be safe to implicitly
132+
// freeze them.
133+
if (_Py_IsImmortal(obj)) {
134+
return Py_MOVABLE_FREEZE;
135+
}
136+
137+
// Immutable objects don't need to be moved
138+
if (_Py_IsImmutable(obj)) {
139+
return Py_MOVABLE_FREEZE;
140+
}
141+
142+
// Types are a pain for regions since it's likely that objects of one type may
143+
// end up in multiple regions, requiring the type to be frozen. Types also
144+
// have a lot of reference pointing to them. Let's hope there is no need to
145+
// keep them freezable
146+
if (PyType_Check(obj)) {
147+
return Py_MOVABLE_FREEZE;
148+
}
149+
150+
// Module objects are also complicated. Freezing them should turn most modules
151+
// into proxys which should make them mostly usable.
152+
if (PyModule_Check(obj)) {
153+
return Py_MOVABLE_FREEZE;
154+
}
155+
156+
// Functions are a mess as well, making the entire system reachable. Freezing
157+
// them should again just magically make most things work
158+
if (PyFunction_Check(obj)) {
159+
return Py_MOVABLE_FREEZE;
160+
}
161+
162+
// CWrappers can't really be owned, but need some special handling since
163+
// interpreters could still race on their RC. Solution, throw them in the
164+
// freezer
165+
if (PyCFunction_Check(obj)
166+
|| Py_IS_TYPE(obj, &_PyMethodWrapper_Type)
167+
|| Py_IS_TYPE(obj, &PyWrapperDescr_Type)
168+
) {
169+
return Py_MOVABLE_FREEZE;
170+
}
171+
172+
// Freezing or moving these objects is... complicated. In some cases it is
173+
// possible but more hassle than it's probably worth. For not we mark them
174+
// all as unmovable.
175+
if (PyFrame_Check(obj)
176+
|| PyGen_CheckExact(obj)
177+
|| PyCoro_CheckExact(obj)
178+
|| PyAsyncGen_CheckExact(obj)
179+
|| PyAsyncGenASend_CheckExact(obj)
180+
) {
181+
return Py_MOVABLE_NO;
182+
}
183+
184+
// Exceptions don't hold anything obviously problematic preventing them
185+
// from being moved into a region. The actual problem is that the runtime
186+
// stores references to them and that these are already emitted on an
187+
// error path. Moving them into a region could add more problems.
188+
// We should discuss how to handle these, maybe freezing is the correct
189+
// approach?
190+
if (PyExceptionInstance_Check(obj)) {
191+
return Py_MOVABLE_NO;
192+
}
193+
194+
// For now, we define all other objects as movable by default. (Surely
195+
// this will not backfire)
196+
return Py_MOVABLE_YES;
197+
}
198+
199+
// ###################################################################
200+
// Tracing Impl
201+
// ###################################################################
202+
203+
typedef struct {
204+
Py_ssize_t objs;
205+
Py_ssize_t incoming_refs;
206+
} trace_res;
207+
208+
static trace_res trace_object(PyObject* obj) {
209+
trace_res res = {
210+
.objs = 1,
211+
.incoming_refs = 2,
212+
};
213+
214+
return res;
215+
}
216+
217+
// ###################################################################
218+
// Region Object
219+
// ###################################################################
4220

5221
typedef struct {
6222
PyObject_HEAD
7223
PyObject *dict;
8224
} TracingRegionObject;
9225

10-
11226
static int
12227
TracingRegion_traverse(TracingRegionObject *self, visitproc visit, void *arg)
13228
{
@@ -30,12 +245,23 @@ TracingRegion_dealloc(TracingRegionObject *self)
30245
Py_TYPE(self)->tp_free((PyObject *)self);
31246
}
32247

33-
static int
34-
TracingRegion_init(TracingRegionObject *self, PyObject *args, PyObject *kwds)
35-
{
36-
return 0;
248+
static PyObject* TracingRegion_trace(PyObject *op) {
249+
trace_res res = trace_object(op);
250+
251+
PyObject *t = Py_BuildValue("(ii)", res.objs, res.incoming_refs);
252+
if (t == NULL) {
253+
return NULL; // propagate Python exception
254+
}
255+
256+
return t;
37257
}
38258

259+
static PyMethodDef TracingRegion_methods[] = {
260+
{"trace", _PyCFunction_CAST(TracingRegion_trace), METH_NOARGS,
261+
"This traces the region and returns the number of incoming references"},
262+
{NULL, NULL} /* sentinel */
263+
};
264+
39265
static PyMemberDef TracingRegion_members[] = {
40266
{"__dict__", _Py_T_OBJECT, offsetof(TracingRegionObject, dict), Py_READONLY},
41267
{NULL}
@@ -50,8 +276,10 @@ PyTypeObject _PyTracingRegion_Type = {
50276
.tp_traverse = (traverseproc)TracingRegion_traverse,
51277
.tp_clear = (inquiry)TracingRegion_clear,
52278
.tp_members = TracingRegion_members,
279+
.tp_methods = TracingRegion_methods,
53280
.tp_dictoffset = offsetof(TracingRegionObject, dict),
54-
.tp_init = (initproc)TracingRegion_init,
55281
.tp_new = PyType_GenericNew,
56282
.tp_reachable = _PyObject_ReachableVisitTypeAndTraverse,
57283
};
284+
285+

0 commit comments

Comments
 (0)