mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
mmc: Tail-call an out-of-memory handler if allocation fails
This commit is contained in:
parent
a0148d4b38
commit
1e41f0e093
2 changed files with 48 additions and 36 deletions
79
src/mmc.c
79
src/mmc.c
|
@ -66,6 +66,7 @@ struct gc_heap {
|
||||||
struct gc_heap_sizer sizer;
|
struct gc_heap_sizer sizer;
|
||||||
struct gc_event_listener event_listener;
|
struct gc_event_listener event_listener;
|
||||||
void *event_listener_data;
|
void *event_listener_data;
|
||||||
|
void* (*allocation_failure)(struct gc_heap*, size_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HEAP_EVENT(heap, event, ...) do { \
|
#define HEAP_EVENT(heap, event, ...) do { \
|
||||||
|
@ -511,20 +512,18 @@ heap_estimate_live_data_after_gc(struct gc_heap *heap,
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
detect_out_of_memory(struct gc_heap *heap, uintptr_t allocation_since_last_gc) {
|
compute_progress(struct gc_heap *heap, uintptr_t allocation_since_last_gc) {
|
||||||
if (heap->sizer.policy != GC_HEAP_SIZE_FIXED)
|
struct nofl_space *nofl = heap_nofl_space(heap);
|
||||||
return;
|
return allocation_since_last_gc > nofl_space_fragmentation(nofl);
|
||||||
|
}
|
||||||
|
|
||||||
if (allocation_since_last_gc > nofl_space_fragmentation(heap_nofl_space(heap)))
|
static int
|
||||||
return;
|
compute_success(struct gc_heap *heap, enum gc_collection_kind gc_kind,
|
||||||
|
int progress) {
|
||||||
if (heap->gc_kind == GC_COLLECTION_MINOR)
|
return progress
|
||||||
return;
|
|| gc_kind == GC_COLLECTION_MINOR
|
||||||
|
|| heap->sizer.policy != GC_HEAP_SIZE_FIXED;
|
||||||
// No allocation since last gc: out of memory.
|
|
||||||
fprintf(stderr, "ran out of space, heap size %zu\n", heap->size);
|
|
||||||
GC_CRASH();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
|
@ -750,12 +749,10 @@ sweep_ephemerons(struct gc_heap *heap) {
|
||||||
return gc_sweep_pending_ephemerons(heap->pending_ephemerons, 0, 1);
|
return gc_sweep_pending_ephemerons(heap->pending_ephemerons, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect(struct gc_mutator *mut,
|
static int collect(struct gc_mutator *mut,
|
||||||
enum gc_collection_kind requested_kind,
|
enum gc_collection_kind requested_kind) GC_NEVER_INLINE;
|
||||||
int requested_by_user) GC_NEVER_INLINE;
|
static int
|
||||||
static void
|
collect(struct gc_mutator *mut, enum gc_collection_kind requested_kind) {
|
||||||
collect(struct gc_mutator *mut, enum gc_collection_kind requested_kind,
|
|
||||||
int requested_by_user) {
|
|
||||||
struct gc_heap *heap = mutator_heap(mut);
|
struct gc_heap *heap = mutator_heap(mut);
|
||||||
struct nofl_space *nofl_space = heap_nofl_space(heap);
|
struct nofl_space *nofl_space = heap_nofl_space(heap);
|
||||||
struct large_object_space *lospace = heap_large_object_space(heap);
|
struct large_object_space *lospace = heap_large_object_space(heap);
|
||||||
|
@ -773,8 +770,7 @@ collect(struct gc_mutator *mut, enum gc_collection_kind requested_kind,
|
||||||
nofl_space_add_to_allocation_counter(nofl_space, &allocation_counter);
|
nofl_space_add_to_allocation_counter(nofl_space, &allocation_counter);
|
||||||
large_object_space_add_to_allocation_counter(lospace, &allocation_counter);
|
large_object_space_add_to_allocation_counter(lospace, &allocation_counter);
|
||||||
heap->total_allocated_bytes_at_last_gc += allocation_counter;
|
heap->total_allocated_bytes_at_last_gc += allocation_counter;
|
||||||
if (!requested_by_user)
|
int progress = compute_progress(heap, allocation_counter);
|
||||||
detect_out_of_memory(heap, allocation_counter);
|
|
||||||
enum gc_collection_kind gc_kind =
|
enum gc_collection_kind gc_kind =
|
||||||
determine_collection_kind(heap, requested_kind);
|
determine_collection_kind(heap, requested_kind);
|
||||||
int is_minor = gc_kind == GC_COLLECTION_MINOR;
|
int is_minor = gc_kind == GC_COLLECTION_MINOR;
|
||||||
|
@ -821,12 +817,12 @@ collect(struct gc_mutator *mut, enum gc_collection_kind requested_kind,
|
||||||
heap->size_at_last_gc = heap->size;
|
heap->size_at_last_gc = heap->size;
|
||||||
HEAP_EVENT(heap, restarting_mutators);
|
HEAP_EVENT(heap, restarting_mutators);
|
||||||
allow_mutators_to_continue(heap);
|
allow_mutators_to_continue(heap);
|
||||||
|
return compute_success(heap, gc_kind, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
trigger_collection(struct gc_mutator *mut,
|
trigger_collection(struct gc_mutator *mut,
|
||||||
enum gc_collection_kind requested_kind,
|
enum gc_collection_kind requested_kind) {
|
||||||
int requested_by_user) {
|
|
||||||
struct gc_heap *heap = mutator_heap(mut);
|
struct gc_heap *heap = mutator_heap(mut);
|
||||||
int prev_kind = -1;
|
int prev_kind = -1;
|
||||||
gc_stack_capture_hot(&mut->stack);
|
gc_stack_capture_hot(&mut->stack);
|
||||||
|
@ -836,14 +832,16 @@ trigger_collection(struct gc_mutator *mut,
|
||||||
heap_lock(heap);
|
heap_lock(heap);
|
||||||
while (mutators_are_stopping(heap))
|
while (mutators_are_stopping(heap))
|
||||||
prev_kind = pause_mutator_for_collection(heap, mut);
|
prev_kind = pause_mutator_for_collection(heap, mut);
|
||||||
|
int success = 1;
|
||||||
if (prev_kind < (int)requested_kind)
|
if (prev_kind < (int)requested_kind)
|
||||||
collect(mut, requested_kind, requested_by_user);
|
success = collect(mut, requested_kind);
|
||||||
heap_unlock(heap);
|
heap_unlock(heap);
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gc_collect(struct gc_mutator *mut, enum gc_collection_kind kind) {
|
gc_collect(struct gc_mutator *mut, enum gc_collection_kind kind) {
|
||||||
trigger_collection(mut, kind, 1);
|
trigger_collection(mut, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
int*
|
int*
|
||||||
|
@ -903,8 +901,10 @@ allocate_large(struct gc_mutator *mut, size_t size,
|
||||||
nofl_space_request_release_memory(nofl_space,
|
nofl_space_request_release_memory(nofl_space,
|
||||||
npages << lospace->page_size_log2);
|
npages << lospace->page_size_log2);
|
||||||
|
|
||||||
while (!nofl_space_shrink(nofl_space, 0))
|
while (!nofl_space_shrink(nofl_space, 0)) {
|
||||||
trigger_collection(mut, GC_COLLECTION_COMPACTING, 0);
|
if (!trigger_collection(mut, GC_COLLECTION_COMPACTING))
|
||||||
|
return heap->allocation_failure(heap, size);
|
||||||
|
}
|
||||||
atomic_fetch_add(&heap->large_object_pages, npages);
|
atomic_fetch_add(&heap->large_object_pages, npages);
|
||||||
|
|
||||||
void *ret = large_object_space_alloc(lospace, npages, kind);
|
void *ret = large_object_space_alloc(lospace, npages, kind);
|
||||||
|
@ -919,7 +919,7 @@ allocate_large(struct gc_mutator *mut, size_t size,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
collect_for_small_allocation(void *mut) {
|
collect_for_small_allocation(void *mut) {
|
||||||
trigger_collection(mut, GC_COLLECTION_ANY, 0);
|
trigger_collection(mut, GC_COLLECTION_ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
|
@ -930,10 +930,16 @@ gc_allocate_slow(struct gc_mutator *mut, size_t size,
|
||||||
if (size > gc_allocator_large_threshold())
|
if (size > gc_allocator_large_threshold())
|
||||||
return allocate_large(mut, size, compute_trace_kind(kind));
|
return allocate_large(mut, size, compute_trace_kind(kind));
|
||||||
|
|
||||||
return gc_ref_heap_object(nofl_allocate(&mut->allocator,
|
struct gc_heap *heap = mutator_heap(mut);
|
||||||
heap_nofl_space(mutator_heap(mut)),
|
while (1) {
|
||||||
size, collect_for_small_allocation,
|
struct gc_ref ret = nofl_allocate(&mut->allocator, heap_nofl_space(heap),
|
||||||
mut, kind));
|
size, kind);
|
||||||
|
if (!gc_ref_is_null(ret))
|
||||||
|
return gc_ref_heap_object(ret);
|
||||||
|
if (trigger_collection(mut, GC_COLLECTION_ANY))
|
||||||
|
continue;
|
||||||
|
return heap->allocation_failure(heap, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1109,6 +1115,12 @@ static void set_heap_size_from_thread(struct gc_heap *heap, size_t size) {
|
||||||
pthread_mutex_unlock(&heap->lock);
|
pthread_mutex_unlock(&heap->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void* allocation_failure(struct gc_heap *heap, size_t size) {
|
||||||
|
fprintf(stderr, "ran out of space, heap size %zu\n", heap->size);
|
||||||
|
GC_CRASH();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
heap_init(struct gc_heap *heap, const struct gc_options *options) {
|
heap_init(struct gc_heap *heap, const struct gc_options *options) {
|
||||||
// *heap is already initialized to 0.
|
// *heap is already initialized to 0.
|
||||||
|
@ -1143,6 +1155,7 @@ heap_init(struct gc_heap *heap, const struct gc_options *options) {
|
||||||
allocation_counter_from_thread,
|
allocation_counter_from_thread,
|
||||||
set_heap_size_from_thread,
|
set_heap_size_from_thread,
|
||||||
heap->background_thread);
|
heap->background_thread);
|
||||||
|
heap->allocation_failure = allocation_failure;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -880,8 +880,7 @@ nofl_allocator_next_hole(struct nofl_allocator *alloc,
|
||||||
|
|
||||||
static struct gc_ref
|
static struct gc_ref
|
||||||
nofl_allocate(struct nofl_allocator *alloc, struct nofl_space *space,
|
nofl_allocate(struct nofl_allocator *alloc, struct nofl_space *space,
|
||||||
size_t size, void (*gc)(void*), void *gc_data,
|
size_t size, enum gc_allocation_kind kind) {
|
||||||
enum gc_allocation_kind kind) {
|
|
||||||
GC_ASSERT(size > 0);
|
GC_ASSERT(size > 0);
|
||||||
GC_ASSERT(size <= gc_allocator_large_threshold());
|
GC_ASSERT(size <= gc_allocator_large_threshold());
|
||||||
size = align_up(size, NOFL_GRANULE_SIZE);
|
size = align_up(size, NOFL_GRANULE_SIZE);
|
||||||
|
@ -894,7 +893,7 @@ nofl_allocate(struct nofl_allocator *alloc, struct nofl_space *space,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!hole)
|
if (!hole)
|
||||||
gc(gc_data);
|
return gc_ref_null();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue