mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
gc.c: add contiguous heap region support (mrb_gc_add_region)
Allow users to provide contiguous memory buffers for GC heap pages via mrb_gc_add_region(). Region pages are carved from user-owned buffers and never freed by the GC. This is the foundation for bitmap GC on embedded targets with fragmented RAM. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ typedef struct mrb_gc {
|
||||
struct mrb_heap_page *heaps; /* all heaps pages */
|
||||
struct mrb_heap_page *free_heaps;/* heaps for allocation */
|
||||
struct mrb_heap_page *sweeps; /* page where sweep starts */
|
||||
struct mrb_heap_region *regions; /* contiguous heap regions */
|
||||
struct RBasic *gray_stack[MRB_GRAY_STACK_SIZE]; /* stack of gray objects */
|
||||
size_t gray_stack_top; /* top index of gray stack */
|
||||
mrb_bool gray_overflow:1; /* gray stack overflowed; needs heap rescan */
|
||||
@@ -69,6 +70,7 @@ typedef struct mrb_gc {
|
||||
} mrb_gc;
|
||||
|
||||
MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object);
|
||||
MRB_API int mrb_gc_add_region(struct mrb_state *mrb, void *start, size_t size);
|
||||
|
||||
#define MRB_GC_RED 7
|
||||
|
||||
|
||||
@@ -166,9 +166,17 @@ typedef struct mrb_heap_page {
|
||||
struct mrb_heap_page *next;
|
||||
struct mrb_heap_page *free_next;
|
||||
mrb_bool old:1;
|
||||
mrb_bool region:1; /* from contiguous region, not malloc */
|
||||
RVALUE objects[MRB_HEAP_PAGE_SIZE];
|
||||
} mrb_heap_page;
|
||||
|
||||
typedef struct mrb_heap_region {
|
||||
struct mrb_heap_region *next;
|
||||
uint8_t *base; /* start of user buffer */
|
||||
size_t size; /* buffer size in bytes */
|
||||
uint16_t page_count; /* pages carved from region */
|
||||
} mrb_heap_region;
|
||||
|
||||
#define GC_STEP_SIZE 1024
|
||||
|
||||
/* white: 001 or 010, black: 100, gray: 000, red:111 */
|
||||
@@ -283,6 +291,17 @@ static mrb_bool
|
||||
heap_p(mrb_gc *gc, const struct RBasic *object)
|
||||
{
|
||||
mrb_heap_page* page;
|
||||
mrb_heap_region *region;
|
||||
|
||||
/* fast path: check contiguous regions via arithmetic */
|
||||
for (region = gc->regions; region; region = region->next) {
|
||||
uintptr_t addr = (uintptr_t)object;
|
||||
uintptr_t base = (uintptr_t)region->base;
|
||||
uintptr_t end = base + (size_t)region->page_count * sizeof(mrb_heap_page);
|
||||
if (addr >= base && addr < end) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
page = gc->heaps;
|
||||
while (page) {
|
||||
@@ -306,9 +325,17 @@ mrb_object_dead_p(mrb_state *mrb, struct RBasic *object)
|
||||
}
|
||||
|
||||
static void
|
||||
add_heap(mrb_state *mrb, mrb_gc *gc)
|
||||
link_heap_page(mrb_gc *gc, mrb_heap_page *page)
|
||||
{
|
||||
page->next = gc->heaps;
|
||||
gc->heaps = page;
|
||||
page->free_next = gc->free_heaps;
|
||||
gc->free_heaps = page;
|
||||
}
|
||||
|
||||
static void
|
||||
init_heap_page(mrb_heap_page *page)
|
||||
{
|
||||
mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page));
|
||||
RVALUE *p, *e;
|
||||
RVALUE *prev = NULL;
|
||||
|
||||
@@ -318,12 +345,50 @@ add_heap(mrb_state *mrb, mrb_gc *gc)
|
||||
prev = p;
|
||||
}
|
||||
page->freelist = prev;
|
||||
}
|
||||
|
||||
page->next = gc->heaps;
|
||||
gc->heaps = page;
|
||||
static void
|
||||
add_heap(mrb_state *mrb, mrb_gc *gc)
|
||||
{
|
||||
mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page));
|
||||
init_heap_page(page);
|
||||
link_heap_page(gc, page);
|
||||
}
|
||||
|
||||
page->free_next = gc->free_heaps;
|
||||
gc->free_heaps = page;
|
||||
MRB_API int
|
||||
mrb_gc_add_region(mrb_state *mrb, void *start, size_t size)
|
||||
{
|
||||
mrb_gc *gc = &mrb->gc;
|
||||
uint8_t *base = (uint8_t*)start;
|
||||
mrb_heap_region *region;
|
||||
uint16_t page_count;
|
||||
uint16_t i;
|
||||
|
||||
/* align base to pointer size */
|
||||
uintptr_t align = sizeof(void*);
|
||||
uintptr_t offset = ((uintptr_t)base + align - 1) & ~(align - 1);
|
||||
size -= (size_t)(offset - (uintptr_t)base);
|
||||
base = (uint8_t*)offset;
|
||||
|
||||
page_count = (uint16_t)(size / sizeof(mrb_heap_page));
|
||||
if (page_count == 0) return 0;
|
||||
|
||||
region = (mrb_heap_region*)mrb_malloc(mrb, sizeof(mrb_heap_region));
|
||||
region->base = base;
|
||||
region->size = size;
|
||||
region->page_count = page_count;
|
||||
region->next = gc->regions;
|
||||
gc->regions = region;
|
||||
|
||||
/* carve pages from the contiguous buffer */
|
||||
for (i = 0; i < page_count; i++) {
|
||||
mrb_heap_page *page = (mrb_heap_page*)(base + (size_t)i * sizeof(mrb_heap_page));
|
||||
memset(page, 0, sizeof(mrb_heap_page));
|
||||
page->region = TRUE;
|
||||
init_heap_page(page);
|
||||
link_heap_page(gc, page);
|
||||
}
|
||||
return page_count;
|
||||
}
|
||||
|
||||
#define DEFAULT_GC_INTERVAL_RATIO 200
|
||||
@@ -345,6 +410,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
|
||||
gc->current_white_part = GC_WHITE_A;
|
||||
gc->heaps = NULL;
|
||||
gc->free_heaps = NULL;
|
||||
gc->regions = NULL;
|
||||
add_heap(mrb, gc);
|
||||
gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
|
||||
gc->step_ratio = DEFAULT_GC_STEP_RATIO;
|
||||
@@ -370,7 +436,9 @@ free_heap(mrb_state *mrb, mrb_gc *gc)
|
||||
if (p->as.free.tt != MRB_TT_FREE)
|
||||
obj_free(mrb, &p->as.basic, TRUE);
|
||||
}
|
||||
mrb_free(mrb, tmp);
|
||||
if (!tmp->region) {
|
||||
mrb_free(mrb, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +446,15 @@ void
|
||||
mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
|
||||
{
|
||||
free_heap(mrb, gc);
|
||||
/* free region descriptors (buffer memory belongs to the caller) */
|
||||
{
|
||||
mrb_heap_region *region = gc->regions;
|
||||
while (region) {
|
||||
mrb_heap_region *next = region->next;
|
||||
mrb_free(mrb, region);
|
||||
region = next;
|
||||
}
|
||||
}
|
||||
#ifndef MRB_GC_FIXED_ARENA
|
||||
mrb_free(mrb, gc->arena);
|
||||
#endif
|
||||
@@ -1120,7 +1197,7 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
|
||||
}
|
||||
|
||||
/* free dead slot */
|
||||
if (dead_slot) {
|
||||
if (dead_slot && !page->region) {
|
||||
mrb_heap_page *next = page->next;
|
||||
|
||||
if (prev) prev->next = next;
|
||||
|
||||
Reference in New Issue
Block a user