From c4bf24ee4672cc24a9355a08eb9afd3dbb073be5 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 6 Jun 2022 15:41:59 -0400 Subject: [PATCH] Remove while loop over heap_prepare Having a while loop over `heap_prepare` makes the GC logic difficult to understand (it is difficult to understand when and why `heap_prepare` yields a free page). It is also a source of bugs and can cause an infinite loop if `heap_page` never yields a free page. --- gc.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/gc.c b/gc.c index 5bce7e1e25..a128e9a93e 100644 --- a/gc.c +++ b/gc.c @@ -2313,24 +2313,67 @@ heap_increment(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *he return FALSE; } +static void +gc_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +{ + /* Continue marking if in incremental marking. */ + if (heap->free_pages == NULL && is_incremental_marking(objspace)) { + gc_marks_continue(objspace, size_pool, heap); + } + + /* Continue sweeping if in lazy sweeping or the previous incremental + * marking finished and did not yield a free page. */ + if (heap->free_pages == NULL && is_lazy_sweeping(objspace)) { + gc_sweep_continue(objspace, size_pool, heap); + } +} + static void heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) { GC_ASSERT(heap->free_pages == NULL); - if (is_incremental_marking(objspace)) { - gc_marks_continue(objspace, size_pool, heap); - } - - if (heap->free_pages == NULL && is_lazy_sweeping(objspace)) { - gc_sweep_continue(objspace, size_pool, heap); - } + /* Continue incremental marking or lazy sweeping, if in any of those steps. */ + gc_continue(objspace, size_pool, heap); + /* If we still don't have a free page and not allowed to create a new page, + * we should start a new GC cycle. */ if (heap->free_pages == NULL && - (will_be_incremental_marking(objspace) || heap_increment(objspace, size_pool, heap) == FALSE) && - gc_start(objspace, GPR_FLAG_NEWOBJ) == FALSE) { - rb_memerror(); + (will_be_incremental_marking(objspace) || + (heap_increment(objspace, size_pool, heap) == FALSE))) { + if (gc_start(objspace, GPR_FLAG_NEWOBJ) == FALSE) { + rb_memerror(); + } + else { + /* Do steps of incremental marking or lazy sweeping if the GC run permits. */ + gc_continue(objspace, size_pool, heap); + + /* If we're not incremental marking (e.g. a minor GC) or finished + * sweeping and still don't have a free page, then + * gc_sweep_finish_size_pool should allow us to create a new page. */ + if (heap->free_pages == NULL && !heap_increment(objspace, size_pool, heap)) { + if (objspace->rgengc.need_major_gc == GPR_FLAG_NONE) { + rb_bug("cannot create a new page after GC"); + } + else { // Major GC is required, which will allow us to create new page + if (gc_start(objspace, GPR_FLAG_NEWOBJ) == FALSE) { + rb_memerror(); + } + else { + /* Do steps of incremental marking or lazy sweeping. */ + gc_continue(objspace, size_pool, heap); + + if (heap->free_pages == NULL && + !heap_increment(objspace, size_pool, heap)) { + rb_bug("cannot create a new page after major GC"); + } + } + } + } + } } + + GC_ASSERT(heap->free_pages != NULL); } void @@ -2530,9 +2573,10 @@ heap_next_free_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_ struct heap_page *page; - while (heap->free_pages == NULL) { + if (heap->free_pages == NULL) { heap_prepare(objspace, size_pool, heap); } + page = heap->free_pages; heap->free_pages = page->free_next;