From 768b93be828baa37193a83df0249c9def5b71fbf Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Wed, 27 Aug 2014 10:45:04 -0600 Subject: [PATCH] improve GC handling of out-of-memory There's a point in attempting to allocate a new large page where it makes sense to GC and try again. --- racket/src/racket/gc2/newgc.c | 79 ++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/racket/src/racket/gc2/newgc.c b/racket/src/racket/gc2/newgc.c index 563925ec99..275ab249f5 100644 --- a/racket/src/racket/gc2/newgc.c +++ b/racket/src/racket/gc2/newgc.c @@ -426,12 +426,17 @@ void check_page_owner(NewGC *gc, const void *p) } #endif -static void *malloc_pages(NewGC *gc, size_t len, size_t alignment, int dirty, int type, int expect_mprotect, void **src_block) +static void *malloc_pages(NewGC *gc, size_t len, size_t alignment, int dirty, int type, int expect_mprotect, + void **src_block, int abort_on_fail) { void *ptr; check_used_against_max(gc, len); ptr = mmu_alloc_page(gc->mmu, len, alignment, dirty, type, expect_mprotect, src_block); - if (!ptr) out_of_memory(); + if (!ptr) { + if (!abort_on_fail) + return NULL; + out_of_memory(); + } #ifdef POINTER_OWNERSHIP_CHECK shared_pagemap_set(ptr, len, gc); @@ -440,6 +445,32 @@ static void *malloc_pages(NewGC *gc, size_t len, size_t alignment, int dirty, in return ptr; } + +static void *malloc_pages_maybe_fail(NewGC *gc, size_t len, size_t alignment, int dirty, int type, int expect_mprotect, + void **src_block, uintptr_t account_size) +{ + void *p; + int tried_gc = 0; + + while (1) { + p = malloc_pages(gc, len, alignment, dirty, type, expect_mprotect, + src_block, gc->in_unsafe_allocation_mode); + if (p) return p; + + /* Try to handle allocation failure. */ + if (!tried_gc) { + if (!gc->avoid_collection) { + collect_now(gc, 1); + gc->gen0.current_size += account_size; + } + tried_gc = 1; + } else if (GC_out_of_memory) + GC_out_of_memory(); + else + out_of_memory(); + } +} + static void free_pages(NewGC *gc, void *p, size_t len, int type, int expect_mprotect, void **src_block) { #ifdef POINTER_OWNERSHIP_CHECK @@ -981,7 +1012,7 @@ static void *allocate_big(const size_t request_size_bytes, int type) mpage *bpage; size_t allocate_size; size_t realpagesize; - void *addr; + void *addr, *src_block; if (GC_gen0_alloc_only) return NULL; @@ -1003,25 +1034,25 @@ static void *allocate_big(const size_t request_size_bytes, int type) aligned for Sparcs. */ allocate_size = COMPUTE_ALLOC_SIZE_FOR_BIG_PAGE_SIZE(request_size_bytes); - gc_if_needed_account_alloc_size(gc, allocate_size); - - /* The following allocations may fail and escape if GC_out_of_memory is set. - We not only need APAGE_SIZE alignment, we + /* We not only need APAGE_SIZE alignment, we need everything consisently mapped within an APAGE_SIZE segment. So round up. */ - - bpage = malloc_mpage(); realpagesize = round_to_apage_size(allocate_size); if (type == PAGE_ATOMIC) - addr = malloc_pages(gc, realpagesize, APAGE_SIZE, MMU_DIRTY, MMU_BIG_MED, MMU_NON_PROTECTABLE, &bpage->mmu_src_block); + addr = malloc_pages_maybe_fail(gc, realpagesize, APAGE_SIZE, MMU_DIRTY, MMU_BIG_MED, MMU_NON_PROTECTABLE, + &src_block, allocate_size); else - addr = malloc_pages(gc, realpagesize, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, MMU_PROTECTABLE, &bpage->mmu_src_block); + addr = malloc_pages_maybe_fail(gc, realpagesize, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, MMU_PROTECTABLE, + &src_block, allocate_size); + + bpage = malloc_mpage(); bpage->addr = addr; bpage->size = allocate_size; bpage->size_class = 2; bpage->page_type = type; + bpage->mmu_src_block = src_block; GCVERBOSEPAGE(gc, "NEW BIG PAGE", bpage); /* push new bpage onto GC->gen0.big_pages */ @@ -1029,7 +1060,6 @@ static void *allocate_big(const size_t request_size_bytes, int type) if(bpage->next) bpage->next->prev = bpage; gc->gen0.big_pages = bpage; - if (gc->saved_allocator) { /* MESSAGE ALLOCATION: orphan this page from the current GC; this page is going to be sent to a different place, so don't account @@ -1051,13 +1081,17 @@ static void *allocate_big(const size_t request_size_bytes, int type) inline static mpage *create_new_medium_page(NewGC *gc, const int sz, const int pos, int type) { mpage *page; int n, ty; + void *src_block, *addr; ty = ((type == PAGE_ATOMIC) ? MED_PAGE_ATOMIC : MED_PAGE_NONATOMIC); + addr = malloc_pages_maybe_fail(gc, APAGE_SIZE, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, + (type == MED_PAGE_NONATOMIC) ? MMU_PROTECTABLE : MMU_NON_PROTECTABLE, + &src_block, sz); + page = malloc_mpage(); - page->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, - (type == MED_PAGE_NONATOMIC) ? MMU_PROTECTABLE : MMU_NON_PROTECTABLE, - &page->mmu_src_block); + page->addr = addr; + page->mmu_src_block = src_block; page->size = sz; page->size_class = 1; page->page_type = PAGE_BIG; @@ -1188,9 +1222,14 @@ static void *allocate_medium(const size_t request_size_bytes, const int type) inline static mpage *gen0_create_new_nursery_mpage(NewGC *gc, const size_t page_size) { mpage *page; + void *addr, *src_block; + + addr = malloc_pages_maybe_fail(gc, page_size, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN0, MMU_NON_PROTECTABLE, + &src_block, page_size); page = malloc_mpage(); - page->addr = malloc_pages(gc, page_size, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN0, MMU_NON_PROTECTABLE, &page->mmu_src_block); + page->addr = addr; + page->mmu_src_block = src_block; page->size_class = 0; page->size = PREFIX_SIZE; GEN0_ALLOC_SIZE(page) = page_size; @@ -2031,7 +2070,7 @@ static void backtrace_new_page(NewGC *gc, mpage *page) only use the first few words: */ page->backtrace = (void **)malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_ZEROED, MMU_BIG_MED, MMU_NON_PROTECTABLE, - &page->backtrace_page_src); + &page->backtrace_page_src, 1); } # define backtrace_new_page_if_needed(gc, page) if (!page->backtrace) backtrace_new_page(gc, page) @@ -3340,7 +3379,8 @@ void GC_mark2(const void *const_p, struct NewGC *gc) int protectable = (type == PAGE_ATOMIC) ? MMU_NON_PROTECTABLE : MMU_PROTECTABLE; /* Allocate and prep the page */ work = malloc_mpage(); - work->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN1, protectable, &work->mmu_src_block); + work->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN1, protectable, + &work->mmu_src_block, 1); work->generation = 1; work->page_type = type; work->size = work->previous_size = PREFIX_SIZE; @@ -3985,7 +4025,8 @@ mpage *allocate_compact_target(NewGC *gc, mpage *work) { mpage *npage; npage = malloc_mpage(); - npage->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN1, page_mmu_protectable(work), &npage->mmu_src_block); + npage->addr = malloc_pages(gc, APAGE_SIZE, APAGE_SIZE, MMU_DIRTY, MMU_SMALL_GEN1, page_mmu_protectable(work), + &npage->mmu_src_block, 1); npage->previous_size = npage->size = PREFIX_SIZE; npage->generation = 1; npage->back_pointers = 0;