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
5221typedef struct {
6222 PyObject_HEAD
7223 PyObject * dict ;
8224} TracingRegionObject ;
9225
10-
11226static int
12227TracingRegion_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+
39265static 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