ruby-changes:72087
From: Peter <ko1@a...>
Date: Tue, 7 Jun 2022 22:56:34 +0900 (JST)
Subject: [ruby-changes:72087] c4bf24ee46 (master): Remove while loop over heap_prepare
https://git.ruby-lang.org/ruby.git/commit/?id=c4bf24ee46 From c4bf24ee4672cc24a9355a08eb9afd3dbb073be5 Mon Sep 17 00:00:00 2001 From: Peter Zhu <peter@p...> Date: Mon, 6 Jun 2022 15:41:59 -0400 Subject: 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 | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/gc.c b/gc.c index 5bce7e1e25..a128e9a93e 100644 --- a/gc.c +++ b/gc.c @@ -2314,23 +2314,66 @@ heap_increment(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *he https://github.com/ruby/ruby/blob/trunk/gc.c#L2314 } static void -heap_prepare(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap) +gc_continue(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)) { + /* 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); + + /* 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_ https://github.com/ruby/ruby/blob/trunk/gc.c#L2573 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; -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/