1010
1111static VALUE Memory_Profiler_Allocations = Qnil ;
1212
13- // Helper to mark object_states table values
14- static int Memory_Profiler_Allocations_object_states_mark (st_data_t key , st_data_t value , st_data_t arg ) {
15- // Don't mark the object, we use it as a key but we never use it as an object, and we don't want to retain it.
16- // VALUE object = (VALUE)key;
17- // rb_gc_mark_movable(object);
18-
13+ // Helper to mark states table (object => state)
14+ static int Memory_Profiler_Allocations_states_mark (st_data_t key , st_data_t value , st_data_t arg ) {
15+ // Don't mark the object key (weak reference - don't keep objects alive)
16+ // Mark the state value
1917 VALUE state = (VALUE )value ;
20- if (!NIL_P (state )) {
21- rb_gc_mark_movable (state );
22- }
18+ rb_gc_mark_movable (state );
2319 return ST_CONTINUE ;
2420}
2521
2622// Foreach callback for st_foreach_with_replace (iteration logic)
27- static int Memory_Profiler_Allocations_object_states_foreach (st_data_t key , st_data_t value , st_data_t argp , int error ) {
23+ static int Memory_Profiler_Allocations_states_foreach (st_data_t key , st_data_t value , st_data_t argp , int error ) {
2824 // Return ST_REPLACE to trigger the replace callback for each entry
2925 return ST_REPLACE ;
3026}
3127
32- // Replace callback for st_foreach_with_replace to update object_states keys and values during compaction
33- static int Memory_Profiler_Allocations_object_states_compact (st_data_t * key , st_data_t * value , st_data_t data , int existing ) {
34- VALUE old_object = (VALUE )* key ;
28+ // Replace callback for st_foreach_with_replace to update states during compaction
29+ static int Memory_Profiler_Allocations_states_compact (st_data_t * key , st_data_t * value , st_data_t data , int existing ) {
30+ // Key is object (VALUE) - we can't update if it moved (would require rehashing/allocation)
3531 VALUE old_state = (VALUE )* value ;
36-
37- VALUE new_object = rb_gc_location (old_object );
3832 VALUE new_state = rb_gc_location (old_state );
3933
40- // Update key if it moved
41- if ( old_object != new_object ) {
42- * key = ( st_data_t ) new_object ;
43- }
34+ // NOTE: We can't update object keys if they moved (would require rehashing/allocation).
35+ // This means lookups may fail after compaction for moved objects.
36+ // This is acceptable - FREEOBJ will simply not find the state and skip the callback.
37+ // The state will be cleaned up when Allocations is freed/cleared.
4438
45- // Update value if it moved
39+ // Update state value if it moved (this is safe, doesn't rehash)
4640 if (old_state != new_state ) {
4741 * value = (st_data_t )new_state ;
4842 }
4943
5044 return ST_CONTINUE ;
5145}
5246
53- // GC mark function for Allocations
5447static void Memory_Profiler_Allocations_mark (void * ptr ) {
5548 struct Memory_Profiler_Capture_Allocations * record = ptr ;
5649
5750 if (!record ) {
5851 return ;
5952 }
6053
61- if (!NIL_P (record -> callback )) {
62- rb_gc_mark_movable (record -> callback );
63- }
54+ rb_gc_mark_movable (record -> callback );
6455
65- // Mark object_states table if it exists
66- if (record -> object_states ) {
67- st_foreach (record -> object_states , Memory_Profiler_Allocations_object_states_mark , 0 );
56+ // Mark states table if it exists
57+ if (record -> states ) {
58+ st_foreach (record -> states , Memory_Profiler_Allocations_states_mark , 0 );
6859 }
6960}
7061
71- // GC free function for Allocations
7262static void Memory_Profiler_Allocations_free (void * ptr ) {
7363 struct Memory_Profiler_Capture_Allocations * record = ptr ;
7464
75- if (record -> object_states ) {
76- st_free_table (record -> object_states );
65+ if (record -> states ) {
66+ st_free_table (record -> states );
7767 }
7868
7969 xfree (record );
@@ -84,14 +74,12 @@ static void Memory_Profiler_Allocations_compact(void *ptr) {
8474 struct Memory_Profiler_Capture_Allocations * record = ptr ;
8575
8676 // Update callback if it moved
87- if (!NIL_P (record -> callback )) {
88- record -> callback = rb_gc_location (record -> callback );
89- }
77+ record -> callback = rb_gc_location (record -> callback );
9078
91- // Update object_states table if it exists
92- if (record -> object_states && record -> object_states -> num_entries > 0 ) {
93- if (st_foreach_with_replace (record -> object_states , Memory_Profiler_Allocations_object_states_foreach , Memory_Profiler_Allocations_object_states_compact , 0 )) {
94- rb_raise (rb_eRuntimeError , "object_states modified during GC compaction" );
79+ // Update states table if it exists
80+ if (record -> states && record -> states -> num_entries > 0 ) {
81+ if (st_foreach_with_replace (record -> states , Memory_Profiler_Allocations_states_foreach , Memory_Profiler_Allocations_states_compact , 0 )) {
82+ rb_raise (rb_eRuntimeError , "states modified during GC compaction" );
9583 }
9684 }
9785}
@@ -158,10 +146,11 @@ void Memory_Profiler_Allocations_clear(VALUE allocations) {
158146 record -> free_count = 0 ; // Reset free count
159147 RB_OBJ_WRITE (allocations , & record -> callback , Qnil ); // Clear callback with write barrier
160148
161- // Clear object states
162- if (record -> object_states ) {
163- st_free_table (record -> object_states );
164- record -> object_states = NULL ;
149+ // Clear states - either clear the table or reinitialize
150+ if (record -> states ) {
151+ st_clear (record -> states );
152+ } else {
153+ record -> states = st_init_numtable ();
165154 }
166155}
167156
0 commit comments