@@ -93,8 +93,18 @@ struct _LivenessState
93
93
register_object_callback filter_callback ;
94
94
WorldStateChanged onWorldStartCallback ;
95
95
WorldStateChanged onWorldStopCallback ;
96
+ guint traverse_depth ; // track recursion. Prevent stack overflow by limiting recurion
96
97
};
97
98
99
+ /* number of sub elements of an array to process before recursing
100
+ * we take a depth first approach to use stack space rather than re-allocating
101
+ * processing array which requires restarting world to ensure allocator lock is not held
102
+ */
103
+ const int kArrayElementsPerChunk = 256 ;
104
+
105
+ /* how far we recurse processing array elements before we stop. Prevents stack overflow */
106
+ const int kMaxTraverseRecursionDepth = 128 ;
107
+
98
108
/* Liveness calculation */
99
109
MONO_API LivenessState * mono_unity_liveness_allocate_struct (MonoClass * filter , guint max_count , register_object_callback callback , void * callback_userdata , WorldStateChanged onWorldStartCallback , WorldStateChanged onWorldStopCallback );
100
110
MONO_API void mono_unity_liveness_stop_gc_world (LivenessState * state );
@@ -185,7 +195,7 @@ static void mono_traverse_generic_object( MonoObject* object, LivenessState* sta
185
195
}
186
196
187
197
188
- static void mono_add_process_object (MonoObject * object , LivenessState * state )
198
+ static gboolean mono_add_process_object (MonoObject * object , LivenessState * state )
189
199
{
190
200
if (object && !IS_MARKED (object ))
191
201
{
@@ -203,8 +213,11 @@ static void mono_add_process_object (MonoObject* object, LivenessState* state)
203
213
if (array_is_full (state -> process_array ))
204
214
array_safe_grow (state , state -> process_array );
205
215
array_push_back (state -> process_array , object );
216
+ return TRUE;
206
217
}
207
218
}
219
+
220
+ return FALSE;
208
221
}
209
222
210
223
static gboolean mono_field_can_contain_references (MonoClassField * field )
@@ -218,11 +231,12 @@ static gboolean mono_field_can_contain_references(MonoClassField* field)
218
231
return MONO_TYPE_IS_REFERENCE (field -> type );
219
232
}
220
233
221
- static void mono_traverse_object_internal (MonoObject * object , gboolean isStruct , MonoClass * klass , LivenessState * state )
234
+ static gboolean mono_traverse_object_internal (MonoObject * object , gboolean isStruct , MonoClass * klass , LivenessState * state )
222
235
{
223
236
int i ;
224
237
MonoClassField * field ;
225
238
MonoClass * p ;
239
+ gboolean added_objects = FALSE;
226
240
227
241
g_assert (object );
228
242
@@ -250,10 +264,10 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
250
264
if (field -> type -> type == MONO_TYPE_GENERICINST )
251
265
{
252
266
g_assert (field -> type -> data .generic_class -> cached_class );
253
- mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .generic_class -> cached_class , state );
267
+ added_objects |= mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .generic_class -> cached_class , state );
254
268
}
255
269
else
256
- mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .klass , state );
270
+ added_objects |= mono_traverse_object_internal ((MonoObject * )offseted , TRUE, field -> type -> data .klass , state );
257
271
continue ;
258
272
}
259
273
@@ -263,10 +277,12 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
263
277
MonoObject * val = NULL ;
264
278
MonoVTable * vtable = NULL ;
265
279
mono_field_get_value (object , field , & val );
266
- mono_add_process_object (val , state );
280
+ added_objects |= mono_add_process_object (val , state );
267
281
}
268
282
}
269
283
}
284
+
285
+ return added_objects ;
270
286
}
271
287
272
288
static void mono_traverse_object (MonoObject * object , LivenessState * state )
@@ -298,16 +314,25 @@ static void mono_traverse_objects (LivenessState* state)
298
314
int i = 0 ;
299
315
MonoObject * object = NULL ;
300
316
317
+ state -> traverse_depth ++ ;
301
318
while (state -> process_array -> len > 0 )
302
319
{
303
320
object = array_pop_back (state -> process_array );
304
321
mono_traverse_generic_object (object , state );
305
322
}
323
+ state -> traverse_depth -- ;
324
+ }
325
+
326
+ static gboolean should_traverse_objects (size_t index , gint32 recursion_depth )
327
+ {
328
+ // Add kArrayElementsPerChunk objects at a time and then traverse
329
+ return ((index + 1 ) & (kArrayElementsPerChunk - 1 )) == 0 &&
330
+ recursion_depth < kMaxTraverseRecursionDepth ;
306
331
}
307
332
308
333
static void mono_traverse_array (MonoArray * array , LivenessState * state )
309
334
{
310
- int i = 0 ;
335
+ size_t i = 0 ;
311
336
gboolean has_references ;
312
337
MonoObject * object = (MonoObject * )array ;
313
338
MonoClass * element_class ;
@@ -333,26 +358,28 @@ static void mono_traverse_array (MonoArray* array, LivenessState* state)
333
358
array_length = mono_array_length (array );
334
359
if (element_class -> valuetype )
335
360
{
361
+ size_t items_processed = 0 ;
336
362
elementClassSize = mono_class_array_element_size (element_class );
337
363
for (i = 0 ; i < array_length ; i ++ )
338
364
{
339
365
MonoObject * object = (MonoObject * )mono_array_addr_with_size (array , elementClassSize , i );
340
- mono_traverse_object_internal (object , 1 , element_class , state );
366
+ if (mono_traverse_object_internal (object , 1 , element_class , state ))
367
+ items_processed ++ ;
341
368
342
- // Add 128 objects at a time and then traverse, 64 seems not be enough
343
- if ( ((i + 1 ) & 127 ) == 0 )
369
+ if (should_traverse_objects (items_processed , state -> traverse_depth ))
344
370
mono_traverse_objects (state );
345
371
}
346
372
}
347
373
else
348
374
{
375
+ size_t items_processed = 0 ;
349
376
for (i = 0 ; i < array_length ; i ++ )
350
377
{
351
378
MonoObject * val = mono_array_get (array , MonoObject * , i );
352
- mono_add_process_object (val , state );
379
+ if (mono_add_process_object (val , state ))
380
+ items_processed ++ ;
353
381
354
- // Add 128 objects at a time and then traverse, 64 seems not be enough
355
- if ( ((i + 1 ) & 127 ) == 0 )
382
+ if (should_traverse_objects (items_processed , state -> traverse_depth ))
356
383
mono_traverse_objects (state );
357
384
}
358
385
}
@@ -572,13 +599,14 @@ LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max
572
599
// process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
573
600
// 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.
574
601
575
- state = g_new (LivenessState , 1 );
602
+ state = g_new0 (LivenessState , 1 );
576
603
max_count = max_count < 1000 ? 1000 : max_count ;
577
604
state -> all_objects = array_create_and_initialize (max_count * 4 );
578
605
state -> process_array = array_create_and_initialize (max_count );
579
606
580
607
state -> first_index_in_all_objects = 0 ;
581
608
state -> filter = filter ;
609
+ state -> traverse_depth = 0 ;
582
610
583
611
state -> callback_userdata = callback_userdata ;
584
612
state -> filter_callback = callback ;
0 commit comments