diff --git a/gc.c b/gc.c index 5edfa7b..3a7b003 100644 --- a/gc.c +++ b/gc.c @@ -39,6 +39,9 @@ static int gc_current_range; static char *gc_free_ptr; static char *gc_range_end; +/* A convenient shorthand */ +#define gc_other_range() (1-gc_current_range) + static value_t gc_weak_box_list; static value_t gc_will_list; static value_t gc_will_active_list; @@ -386,17 +389,22 @@ void gc_init(size_t min_size, size_t max_size) gc_soft_limit = gc_min_size; gc_range_end = gc_free_ptr + gc_soft_limit; + gc_weak_box_list = NIL; + gc_will_list = NIL; + gc_will_active_list = NIL; + + clear_gc_stats(); + + gc_enabled = true; +} + +void clear_gc_stats(void) +{ gc_stats.collections = 0; gc_stats.total_ns = 0; gc_stats.total_freed = 0; gc_stats.high_water = 0; gc_stats.max_ns = 0; - - gc_weak_box_list = NIL; - gc_will_list = NIL; - gc_will_active_list = NIL; - - gc_enabled = true; } /* Preconditions: nbytes pre-aligned a la gc_align(), and space exists. */ @@ -552,7 +560,7 @@ static size_t transfer_children(object_t *obj) static void swap_gc_ranges(void) { - gc_current_range = 1 - gc_current_range; + gc_current_range = gc_other_range(); gc_free_ptr = gc_ranges[gc_current_range]; gc_range_end = gc_free_ptr + gc_soft_limit; } @@ -721,6 +729,13 @@ static void update_soft_limit(size_t min_free) #endif } +static void _out_of_memory(void) __attribute__ ((noreturn)); +static void _out_of_memory(void) +{ + out_of_memory(); + abort(); +} + static void _collect_garbage(size_t min_free) { if (gc_enabled) @@ -772,7 +787,7 @@ static void _collect_garbage(size_t min_free) #ifndef NDEBUG /* Clear old range, to make it easier to detect bugs. */ - memset(gc_ranges[1-gc_current_range], 0, gc_soft_limit); + memset(gc_ranges[gc_other_range()], 0, gc_soft_limit); #endif //debug(("Finished collection with %d bytes to spare (out of %d bytes).\n", gc_free_space(), gc_soft_limit)); @@ -803,7 +818,43 @@ static void _collect_garbage(size_t min_free) if (gc_free_space() < min_free) { - out_of_memory(); + size_t bytes_used = gc_free_ptr - gc_ranges[gc_current_range]; + size_t need_bytes = bytes_used + min_free; + + /* If GC is disabled then we can't move anything, so reallocating is impossible. */ + if (!gc_enabled) + _out_of_memory(); + + /* + * Try to get more memory from the C runtime. + */ + + debug(("Ran out of free memory; will try to allocate more...\n")); + + do { + release_assert(gc_max_size < (SIZE_MAX/2)); + gc_max_size *= 2; + } while (gc_max_size < need_bytes); + + /* Reallocate the unused space. */ + free(gc_ranges[gc_other_range()]); + gc_ranges[gc_other_range()] = (char*)malloc(gc_max_size); + + /* See if reallocation succeeded. */ + if (!gc_ranges[gc_other_range()]) + _out_of_memory(); + + /* Move everything into the newly enlarged space. + * Will update the soft-limit. */ + _collect_garbage(0); + + /* Reallocate the other space, now unused. */ + free(gc_ranges[gc_other_range()]); + gc_ranges[gc_other_range()] = (char*)malloc(gc_max_size); + + /* Ensure second reallocation succeeded. */ + if (!gc_ranges[gc_other_range()]) + _out_of_memory(); } } @@ -976,17 +1027,27 @@ void fprint_value(FILE *f, value_t v) void fprint_gc_stats(FILE *f) { - const double total_time = gc_stats.total_ns / 1.0e9; - const double max_time = gc_stats.max_ns / 1.0e9; + if (gc_stats.collections > 0) + { + const double total_time = gc_stats.total_ns / 1.0e9; + const double max_time = gc_stats.max_ns / 1.0e9; - fprintf(f, "%lld bytes freed in %0.6f sec => %0.3f MB/sec. (%d GCs.)\n", - gc_stats.total_freed, - total_time, - (gc_stats.total_freed / total_time) / (1024*1024), - gc_stats.collections); + fprintf(f, "GC: %lld bytes freed by %d GCs in %0.6f sec => %0.3f MB/sec.\n", + gc_stats.total_freed, + gc_stats.collections, + total_time, + (gc_stats.total_freed / total_time) / (1024*1024)); - fprintf(f, "Max GC time was %0.6f sec, avg. %0.6f sec; peak heap size was %d bytes.\n", - max_time, (total_time / gc_stats.collections), gc_stats.high_water); + fprintf(f, "GC: Avg. time was %0.6f sec, max %0.6f.\n", + (total_time / gc_stats.collections), max_time); + + fprintf(f, "GC: The soft-limit peaked at %d bytes out of %d allocated.\n", + gc_stats.high_water, gc_max_size); + } + else + { + fputs("GC: No garbage collection was performed.\n", f); + } } /* vim:set sw=2 expandtab: */ diff --git a/gc.h b/gc.h index bacc2cc..534d381 100644 --- a/gc.h +++ b/gc.h @@ -376,6 +376,7 @@ static inline builtin_fn_t *_get_builtin_fn(value_t v) } void gc_init(size_t min_size, size_t max_size); +void clear_gc_stats(void); void register_gc_root(gc_root_t *root, value_t v); void unregister_gc_root(gc_root_t *root); void *gc_alloc(size_t nbytes); @@ -398,7 +399,7 @@ static inline void print_gc_stats(void) void _release_assert(bool expr, const char *str, const char *file, int line); /* To be provided by the main application */ -void out_of_memory(void) __attribute__ ((noreturn)); +void out_of_memory(void); #endif /* vim:set sw=2 expandtab: */ diff --git a/rosella.c b/rosella.c index 93c1dc5..bf6bf5e 100644 --- a/rosella.c +++ b/rosella.c @@ -31,7 +31,7 @@ int main(int argc, char **argv) { srand((unsigned int)time(NULL)); - gc_init(1024, 256*1024*1024); + gc_init(256*1024, 1024*1024); builtin_init(); interpreter_init(); @@ -73,6 +73,10 @@ int main(int argc, char **argv) print_value(CAR(result)); nl(); } + + nl(); + fflush(stdout); + print_gc_stats(); } else { @@ -137,10 +141,12 @@ static void test_garbage_collection(bool keep_going) while (1) { - int r = rand() & 0xffff; + int r = rand() & 0x1ffff; if (r == 0) + { root.value = fixnum_value(rand()); + } else { switch (r & 7) @@ -173,14 +179,10 @@ static void test_garbage_collection(bool keep_going) if (++count >= 50000000) { - nl(); print_gc_stats(); + nl(); - gc_stats.collections = 0; - gc_stats.total_ns = 0; - gc_stats.total_freed = 0; - gc_stats.high_water = 0; - gc_stats.max_ns = 0; + clear_gc_stats(); count = 0; if (!keep_going) @@ -196,6 +198,9 @@ static void test_reader(void) value_t v; do { + fputs("> ", stdout); + fflush(stdout); + v = read_value(stdin); if (is_struct(v) && _get_struct(v)->type == lookup_builtin(BI_LAMBDA)) {