@@ -72,8 +72,18 @@ struct _LivenessState
72
72
register_object_callback filter_callback ;
73
73
WorldStateChanged onWorldStartCallback ;
74
74
WorldStateChanged onWorldStopCallback ;
75
+ guint traverse_depth ; // track recursion. Prevent stack overflow by limiting recurion
75
76
};
76
77
78
+ /* number of sub elements of an array to process before recursing
79
+ * we take a depth first approach to use stack space rather than re-allocating
80
+ * processing array which requires restarting world to ensure allocator lock is not held
81
+ */
82
+ const int kArrayElementsPerChunk = 256 ;
83
+
84
+ /* how far we recurse processing array elements before we stop. Prevents stack overflow */
85
+ const int kMaxTraverseRecursionDepth = 128 ;
86
+
77
87
/* Liveness calculation */
78
88
LivenessState * mono_unity_liveness_allocate_struct (MonoClass * filter , guint max_count , register_object_callback callback , void * callback_userdata , WorldStateChanged onWorldStartCallback , WorldStateChanged onWorldStopCallback );
79
89
void mono_unity_liveness_stop_gc_world (LivenessState * state );
@@ -161,7 +171,7 @@ static void mono_traverse_generic_object( MonoObject* object, LivenessState* sta
161
171
}
162
172
163
173
164
- static void mono_add_process_object (MonoObject * object , LivenessState * state )
174
+ static gboolean mono_add_process_object (MonoObject * object , LivenessState * state )
165
175
{
166
176
if (object && !IS_MARKED (object ))
167
177
{
@@ -179,8 +189,11 @@ static void mono_add_process_object (MonoObject* object, LivenessState* state)
179
189
if (array_is_full (state -> process_array ))
180
190
array_safe_grow (state , state -> process_array );
181
191
array_push_back (state -> process_array , object );
192
+ return TRUE;
182
193
}
183
194
}
195
+
196
+ return FALSE;
184
197
}
185
198
186
199
static gboolean mono_field_can_contain_references (MonoClassField * field )
@@ -194,11 +207,12 @@ static gboolean mono_field_can_contain_references(MonoClassField* field)
194
207
return MONO_TYPE_IS_REFERENCE (field -> type );
195
208
}
196
209
197
- static void mono_traverse_object_internal (MonoObject * object , gboolean isStruct , MonoClass * klass , LivenessState * state )
210
+ static gboolean mono_traverse_object_internal (MonoObject * object , gboolean isStruct , MonoClass * klass , LivenessState * state )
198
211
{
199
212
int i ;
200
213
MonoClassField * field ;
201
214
MonoClass * p ;
215
+ gboolean added_objects = FALSE;
202
216
203
217
g_assert (object );
204
218
@@ -226,10 +240,10 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
226
240
if (field -> type -> type == MONO_TYPE_GENERICINST )
227
241
{
228
242
g_assert (field -> type -> data .generic_class -> cached_class );
229
- mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .generic_class -> cached_class , state );
243
+ added_objects |= mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .generic_class -> cached_class , state );
230
244
}
231
245
else
232
- mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .klass , state );
246
+ added_objects |= mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .klass , state );
233
247
continue ;
234
248
}
235
249
@@ -239,10 +253,12 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
239
253
MonoObject * val = NULL ;
240
254
MonoVTable * vtable = NULL ;
241
255
mono_field_get_value (object , field , & val );
242
- mono_add_process_object (val , state );
256
+ added_objects |= mono_add_process_object (val , state );
243
257
}
244
258
}
245
259
}
260
+
261
+ return added_objects ;
246
262
}
247
263
248
264
static void mono_traverse_object (MonoObject * object , LivenessState * state )
@@ -274,16 +290,25 @@ static void mono_traverse_objects (LivenessState* state)
274
290
int i = 0 ;
275
291
MonoObject * object = NULL ;
276
292
293
+ state -> traverse_depth ++ ;
277
294
while (state -> process_array -> len > 0 )
278
295
{
279
296
object = array_pop_back (state -> process_array );
280
297
mono_traverse_generic_object (object , state );
281
298
}
299
+ state -> traverse_depth -- ;
300
+ }
301
+
302
+ static gboolean should_traverse_objects (size_t index , gint32 recursion_depth )
303
+ {
304
+ // Add kArrayElementsPerChunk objects at a time and then traverse
305
+ return ((index + 1 ) & (kArrayElementsPerChunk - 1 )) == 0 &&
306
+ recursion_depth < kMaxTraverseRecursionDepth ;
282
307
}
283
308
284
309
static void mono_traverse_array (MonoArray * array , LivenessState * state )
285
310
{
286
- int i = 0 ;
311
+ size_t i = 0 ;
287
312
gboolean has_references ;
288
313
MonoObject * object = (MonoObject * )array ;
289
314
MonoClass * element_class ;
@@ -309,26 +334,28 @@ static void mono_traverse_array (MonoArray* array, LivenessState* state)
309
334
array_length = mono_array_length (array );
310
335
if (element_class -> valuetype )
311
336
{
337
+ size_t items_processed = 0 ;
312
338
elementClassSize = mono_class_array_element_size (element_class );
313
339
for (i = 0 ; i < array_length ; i ++ )
314
340
{
315
341
MonoObject * object = (MonoObject * )mono_array_addr_with_size (array , elementClassSize , i );
316
- mono_traverse_object_internal (object , 1 , element_class , state );
342
+ if (mono_traverse_object_internal (object , 1 , element_class , state ))
343
+ items_processed ++ ;
317
344
318
- // Add 128 objects at a time and then traverse, 64 seems not be enough
319
- if ( ((i + 1 ) & 127 ) == 0 )
345
+ if (should_traverse_objects (items_processed , state -> traverse_depth ))
320
346
mono_traverse_objects (state );
321
347
}
322
348
}
323
349
else
324
350
{
351
+ size_t items_processed = 0 ;
325
352
for (i = 0 ; i < array_length ; i ++ )
326
353
{
327
354
MonoObject * val = mono_array_get (array , MonoObject * , i );
328
- mono_add_process_object (val , state );
355
+ if (mono_add_process_object (val , state ))
356
+ items_processed ++ ;
329
357
330
- // Add 128 objects at a time and then traverse, 64 seems not be enough
331
- if ( ((i + 1 ) & 127 ) == 0 )
358
+ if (should_traverse_objects (items_processed , state -> traverse_depth ))
332
359
mono_traverse_objects (state );
333
360
}
334
361
}
@@ -541,13 +568,14 @@ LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max
541
568
// process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
542
569
// if all_objects run out of space, run through list, add objects that match the filter, clear bit in vtable and then clear the array.
543
570
544
- state = g_new (LivenessState , 1 );
571
+ state = g_new0 (LivenessState , 1 );
545
572
max_count = max_count < 1000 ? 1000 : max_count ;
546
573
state -> all_objects = array_create_and_initialize (max_count * 4 );
547
574
state -> process_array = array_create_and_initialize (max_count );
548
575
549
576
state -> first_index_in_all_objects = 0 ;
550
577
state -> filter = filter ;
578
+ state -> traverse_depth = 0 ;
551
579
552
580
state -> callback_userdata = callback_userdata ;
553
581
state -> filter_callback = callback ;
0 commit comments