@@ -2,75 +2,265 @@ pub(crate) use gc::module_def;
22
33#[ pymodule]
44mod gc {
5- use crate :: vm:: { PyResult , VirtualMachine , function:: FuncArgs } ;
5+ use crate :: vm:: {
6+ PyObjectRef , PyResult , VirtualMachine ,
7+ builtins:: PyListRef ,
8+ function:: { FuncArgs , OptionalArg } ,
9+ gc_state,
10+ } ;
611
12+ // Debug flag constants
13+ #[ pyattr]
14+ const DEBUG_STATS : u32 = gc_state:: GcDebugFlags :: STATS . bits ( ) ;
15+ #[ pyattr]
16+ const DEBUG_COLLECTABLE : u32 = gc_state:: GcDebugFlags :: COLLECTABLE . bits ( ) ;
17+ #[ pyattr]
18+ const DEBUG_UNCOLLECTABLE : u32 = gc_state:: GcDebugFlags :: UNCOLLECTABLE . bits ( ) ;
19+ #[ pyattr]
20+ const DEBUG_SAVEALL : u32 = gc_state:: GcDebugFlags :: SAVEALL . bits ( ) ;
21+ #[ pyattr]
22+ const DEBUG_LEAK : u32 = gc_state:: GcDebugFlags :: LEAK . bits ( ) ;
23+
24+ /// Enable automatic garbage collection.
25+ #[ pyfunction]
26+ fn enable ( ) {
27+ gc_state:: gc_state ( ) . enable ( ) ;
28+ }
29+
30+ /// Disable automatic garbage collection.
31+ #[ pyfunction]
32+ fn disable ( ) {
33+ gc_state:: gc_state ( ) . disable ( ) ;
34+ }
35+
36+ /// Return true if automatic gc is enabled.
37+ #[ pyfunction]
38+ fn isenabled ( ) -> bool {
39+ gc_state:: gc_state ( ) . is_enabled ( )
40+ }
41+
42+ /// Run a garbage collection. Returns the number of unreachable objects found.
43+ #[ derive( FromArgs ) ]
44+ struct CollectArgs {
45+ #[ pyarg( any, optional) ]
46+ generation : OptionalArg < i32 > ,
47+ }
48+
49+ #[ pyfunction]
50+ fn collect ( args : CollectArgs , vm : & VirtualMachine ) -> PyResult < i32 > {
51+ let generation = args. generation ;
52+ let generation_num = generation. unwrap_or ( 2 ) ;
53+ if !( 0 ..=2 ) . contains ( & generation_num) {
54+ return Err ( vm. new_value_error ( "invalid generation" . to_owned ( ) ) ) ;
55+ }
56+
57+ // Invoke callbacks with "start" phase
58+ invoke_callbacks ( vm, "start" , generation_num as usize , 0 , 0 ) ;
59+
60+ // Manual gc.collect() should run even if GC is disabled
61+ let gc = gc_state:: gc_state ( ) ;
62+ let ( collected, uncollectable) = gc. collect_force ( generation_num as usize ) ;
63+
64+ // Move objects from gc_state.garbage to vm.ctx.gc_garbage (for DEBUG_SAVEALL)
65+ {
66+ let mut state_garbage = gc. garbage . lock ( ) ;
67+ if !state_garbage. is_empty ( ) {
68+ let py_garbage = & vm. ctx . gc_garbage ;
69+ let mut garbage_vec = py_garbage. borrow_vec_mut ( ) ;
70+ for obj in state_garbage. drain ( ..) {
71+ garbage_vec. push ( obj) ;
72+ }
73+ }
74+ }
75+
76+ // Invoke callbacks with "stop" phase
77+ invoke_callbacks (
78+ vm,
79+ "stop" ,
80+ generation_num as usize ,
81+ collected,
82+ uncollectable,
83+ ) ;
84+
85+ Ok ( collected as i32 )
86+ }
87+
88+ /// Return the current collection thresholds as a tuple.
789 #[ pyfunction]
8- fn collect ( _args : FuncArgs , _vm : & VirtualMachine ) -> i32 {
9- 0
90+ fn get_threshold ( vm : & VirtualMachine ) -> PyObjectRef {
91+ let ( t0, t1, t2) = gc_state:: gc_state ( ) . get_threshold ( ) ;
92+ vm. ctx
93+ . new_tuple ( vec ! [
94+ vm. ctx. new_int( t0) . into( ) ,
95+ vm. ctx. new_int( t1) . into( ) ,
96+ vm. ctx. new_int( t2) . into( ) ,
97+ ] )
98+ . into ( )
1099 }
11100
101+ /// Set the collection thresholds.
12102 #[ pyfunction]
13- fn isenabled ( _args : FuncArgs , _vm : & VirtualMachine ) -> bool {
14- false
103+ fn set_threshold ( threshold0 : u32 , threshold1 : OptionalArg < u32 > , threshold2 : OptionalArg < u32 > ) {
104+ gc_state:: gc_state ( ) . set_threshold (
105+ threshold0,
106+ threshold1. into_option ( ) ,
107+ threshold2. into_option ( ) ,
108+ ) ;
15109 }
16110
111+ /// Return the current collection counts as a tuple.
17112 #[ pyfunction]
18- fn enable ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
19- Err ( vm. new_not_implemented_error ( "" ) )
113+ fn get_count ( vm : & VirtualMachine ) -> PyObjectRef {
114+ let ( c0, c1, c2) = gc_state:: gc_state ( ) . get_count ( ) ;
115+ vm. ctx
116+ . new_tuple ( vec ! [
117+ vm. ctx. new_int( c0) . into( ) ,
118+ vm. ctx. new_int( c1) . into( ) ,
119+ vm. ctx. new_int( c2) . into( ) ,
120+ ] )
121+ . into ( )
20122 }
21123
124+ /// Return the current debugging flags.
22125 #[ pyfunction]
23- fn disable ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
24- Err ( vm . new_not_implemented_error ( "" ) )
126+ fn get_debug ( ) -> u32 {
127+ gc_state :: gc_state ( ) . get_debug ( ) . bits ( )
25128 }
26129
130+ /// Set the debugging flags.
27131 #[ pyfunction]
28- fn get_count ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
29- Err ( vm . new_not_implemented_error ( "" ) )
132+ fn set_debug ( flags : u32 ) {
133+ gc_state :: gc_state ( ) . set_debug ( gc_state :: GcDebugFlags :: from_bits_truncate ( flags ) ) ;
30134 }
31135
136+ /// Return a list of per-generation gc stats.
32137 #[ pyfunction]
33- fn get_debug ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
34- Err ( vm. new_not_implemented_error ( "" ) )
138+ fn get_stats ( vm : & VirtualMachine ) -> PyResult < PyListRef > {
139+ let stats = gc_state:: gc_state ( ) . get_stats ( ) ;
140+ let mut result = Vec :: with_capacity ( 3 ) ;
141+
142+ for stat in stats. iter ( ) {
143+ let dict = vm. ctx . new_dict ( ) ;
144+ dict. set_item ( "collections" , vm. ctx . new_int ( stat. collections ) . into ( ) , vm) ?;
145+ dict. set_item ( "collected" , vm. ctx . new_int ( stat. collected ) . into ( ) , vm) ?;
146+ dict. set_item (
147+ "uncollectable" ,
148+ vm. ctx . new_int ( stat. uncollectable ) . into ( ) ,
149+ vm,
150+ ) ?;
151+ result. push ( dict. into ( ) ) ;
152+ }
153+
154+ Ok ( vm. ctx . new_list ( result) )
155+ }
156+
157+ /// Return the list of objects tracked by the collector.
158+ #[ derive( FromArgs ) ]
159+ struct GetObjectsArgs {
160+ #[ pyarg( any, optional) ]
161+ generation : OptionalArg < Option < i32 > > ,
35162 }
36163
37164 #[ pyfunction]
38- fn get_objects ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
39- Err ( vm. new_not_implemented_error ( "" ) )
165+ fn get_objects ( args : GetObjectsArgs , vm : & VirtualMachine ) -> PyResult < PyListRef > {
166+ let generation_opt = args. generation . flatten ( ) ;
167+ if let Some ( g) = generation_opt
168+ && !( 0 ..=2 ) . contains ( & g)
169+ {
170+ return Err ( vm. new_value_error ( format ! ( "generation must be in range(0, 3), not {}" , g) ) ) ;
171+ }
172+ let objects = gc_state:: gc_state ( ) . get_objects ( generation_opt) ;
173+ Ok ( vm. ctx . new_list ( objects) )
40174 }
41175
176+ /// Return the list of objects directly referred to by any of the arguments.
42177 #[ pyfunction]
43- fn get_referents ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
44- Err ( vm. new_not_implemented_error ( "" ) )
178+ fn get_referents ( args : FuncArgs , vm : & VirtualMachine ) -> PyListRef {
179+ let mut result = Vec :: new ( ) ;
180+
181+ for obj in args. args {
182+ // Use the gc_get_referents method to get references
183+ result. extend ( obj. gc_get_referents ( ) ) ;
184+ }
185+
186+ vm. ctx . new_list ( result)
45187 }
46188
189+ /// Return the list of objects that directly refer to any of the arguments.
47190 #[ pyfunction]
48- fn get_referrers ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
49- Err ( vm. new_not_implemented_error ( "" ) )
191+ fn get_referrers ( args : FuncArgs , vm : & VirtualMachine ) -> PyListRef {
192+ // This is expensive: we need to scan all tracked objects
193+ // For now, return an empty list (would need full object tracking to implement)
194+ let _ = args;
195+ vm. ctx . new_list ( vec ! [ ] )
50196 }
51197
198+ /// Return True if the object is tracked by the garbage collector.
52199 #[ pyfunction]
53- fn get_stats ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
54- Err ( vm. new_not_implemented_error ( "" ) )
200+ fn is_tracked ( obj : PyObjectRef ) -> bool {
201+ // An object is tracked if it has IS_TRACE = true (has a trace function)
202+ obj. is_gc_tracked ( )
55203 }
56204
205+ /// Return True if the object has been finalized by the garbage collector.
57206 #[ pyfunction]
58- fn get_threshold ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
59- Err ( vm. new_not_implemented_error ( "" ) )
207+ fn is_finalized ( obj : PyObjectRef ) -> bool {
208+ // Check the per-object finalized flag directly
209+ obj. gc_finalized ( )
60210 }
61211
212+ /// Freeze all objects tracked by gc.
62213 #[ pyfunction]
63- fn is_tracked ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
64- Err ( vm . new_not_implemented_error ( "" ) )
214+ fn freeze ( ) {
215+ gc_state :: gc_state ( ) . freeze ( ) ;
65216 }
66217
218+ /// Unfreeze all objects in the permanent generation.
67219 #[ pyfunction]
68- fn set_debug ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
69- Err ( vm . new_not_implemented_error ( "" ) )
220+ fn unfreeze ( ) {
221+ gc_state :: gc_state ( ) . unfreeze ( ) ;
70222 }
71223
224+ /// Return the number of objects in the permanent generation.
72225 #[ pyfunction]
73- fn set_threshold ( _args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
74- Err ( vm. new_not_implemented_error ( "" ) )
226+ fn get_freeze_count ( ) -> usize {
227+ gc_state:: gc_state ( ) . get_freeze_count ( )
228+ }
229+
230+ /// gc.garbage - list of uncollectable objects
231+ #[ pyattr]
232+ fn garbage ( vm : & VirtualMachine ) -> PyListRef {
233+ vm. ctx . gc_garbage . clone ( )
234+ }
235+
236+ /// gc.callbacks - list of callbacks to be invoked
237+ #[ pyattr]
238+ fn callbacks ( vm : & VirtualMachine ) -> PyListRef {
239+ vm. ctx . gc_callbacks . clone ( )
240+ }
241+
242+ /// Helper function to invoke GC callbacks
243+ fn invoke_callbacks (
244+ vm : & VirtualMachine ,
245+ phase : & str ,
246+ generation : usize ,
247+ collected : usize ,
248+ uncollectable : usize ,
249+ ) {
250+ let callbacks_list = & vm. ctx . gc_callbacks ;
251+ let callbacks: Vec < PyObjectRef > = callbacks_list. borrow_vec ( ) . to_vec ( ) ;
252+ if callbacks. is_empty ( ) {
253+ return ;
254+ }
255+
256+ let phase_str: PyObjectRef = vm. ctx . new_str ( phase) . into ( ) ;
257+ let info = vm. ctx . new_dict ( ) ;
258+ let _ = info. set_item ( "generation" , vm. ctx . new_int ( generation) . into ( ) , vm) ;
259+ let _ = info. set_item ( "collected" , vm. ctx . new_int ( collected) . into ( ) , vm) ;
260+ let _ = info. set_item ( "uncollectable" , vm. ctx . new_int ( uncollectable) . into ( ) , vm) ;
261+
262+ for callback in callbacks {
263+ let _ = callback. call ( ( phase_str. clone ( ) , info. clone ( ) ) , vm) ;
264+ }
75265 }
76266}
0 commit comments