Skip to content

Commit 3f48b95

Browse files
committed
Fix liveness recursion issue (case 1137077). Cherry-pick of 0fb8f1f
1 parent acf432a commit 3f48b95

File tree

1 file changed

+41
-13
lines changed

1 file changed

+41
-13
lines changed

mono/metadata/unity-liveness.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,18 @@ struct _LivenessState
9393
register_object_callback filter_callback;
9494
WorldStateChanged onWorldStartCallback;
9595
WorldStateChanged onWorldStopCallback;
96+
guint traverse_depth; // track recursion. Prevent stack overflow by limiting recurion
9697
};
9798

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+
98108
/* Liveness calculation */
99109
MONO_API LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback);
100110
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
185195
}
186196

187197

188-
static void mono_add_process_object (MonoObject* object, LivenessState* state)
198+
static gboolean mono_add_process_object (MonoObject* object, LivenessState* state)
189199
{
190200
if (object && !IS_MARKED(object))
191201
{
@@ -203,8 +213,11 @@ static void mono_add_process_object (MonoObject* object, LivenessState* state)
203213
if(array_is_full(state->process_array))
204214
array_safe_grow(state, state->process_array);
205215
array_push_back(state->process_array, object);
216+
return TRUE;
206217
}
207218
}
219+
220+
return FALSE;
208221
}
209222

210223
static gboolean mono_field_can_contain_references(MonoClassField* field)
@@ -218,11 +231,12 @@ static gboolean mono_field_can_contain_references(MonoClassField* field)
218231
return MONO_TYPE_IS_REFERENCE(field->type);
219232
}
220233

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)
222235
{
223236
int i;
224237
MonoClassField *field;
225238
MonoClass *p;
239+
gboolean added_objects = FALSE;
226240

227241
g_assert (object);
228242

@@ -250,10 +264,10 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
250264
if (field->type->type == MONO_TYPE_GENERICINST)
251265
{
252266
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);
254268
}
255269
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);
257271
continue;
258272
}
259273

@@ -263,10 +277,12 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
263277
MonoObject* val = NULL;
264278
MonoVTable *vtable = NULL;
265279
mono_field_get_value (object, field, &val);
266-
mono_add_process_object (val, state);
280+
added_objects |= mono_add_process_object (val, state);
267281
}
268282
}
269283
}
284+
285+
return added_objects;
270286
}
271287

272288
static void mono_traverse_object (MonoObject* object, LivenessState* state)
@@ -298,16 +314,25 @@ static void mono_traverse_objects (LivenessState* state)
298314
int i = 0;
299315
MonoObject* object = NULL;
300316

317+
state->traverse_depth++;
301318
while (state->process_array->len > 0)
302319
{
303320
object = array_pop_back(state->process_array);
304321
mono_traverse_generic_object(object, state);
305322
}
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;
306331
}
307332

308333
static void mono_traverse_array (MonoArray* array, LivenessState* state)
309334
{
310-
int i = 0;
335+
size_t i = 0;
311336
gboolean has_references;
312337
MonoObject* object = (MonoObject*)array;
313338
MonoClass* element_class;
@@ -333,26 +358,28 @@ static void mono_traverse_array (MonoArray* array, LivenessState* state)
333358
array_length = mono_array_length (array);
334359
if (element_class->valuetype)
335360
{
361+
size_t items_processed = 0;
336362
elementClassSize = mono_class_array_element_size (element_class);
337363
for (i = 0; i < array_length; i++)
338364
{
339365
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++;
341368

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))
344370
mono_traverse_objects(state);
345371
}
346372
}
347373
else
348374
{
375+
size_t items_processed = 0;
349376
for (i = 0; i < array_length; i++)
350377
{
351378
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++;
353381

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))
356383
mono_traverse_objects(state);
357384
}
358385
}
@@ -572,13 +599,14 @@ LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max
572599
// process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
573600
// 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.
574601

575-
state = g_new(LivenessState, 1);
602+
state = g_new0(LivenessState, 1);
576603
max_count = max_count < 1000 ? 1000 : max_count;
577604
state->all_objects = array_create_and_initialize(max_count*4);
578605
state->process_array = array_create_and_initialize (max_count);
579606

580607
state->first_index_in_all_objects = 0;
581608
state->filter = filter;
609+
state->traverse_depth = 0;
582610

583611
state->callback_userdata = callback_userdata;
584612
state->filter_callback = callback;

0 commit comments

Comments
 (0)