diff --git a/src/adaptive-heap-sizer.h b/src/adaptive-heap-sizer.h index 796f07469..126a493b8 100644 --- a/src/adaptive-heap-sizer.h +++ b/src/adaptive-heap-sizer.h @@ -7,6 +7,7 @@ #include #include "assert.h" +#include "background-thread.h" #include "debug.h" #include "heap-sizer.h" #include "gc-platform.h" @@ -37,10 +38,10 @@ struct gc_adaptive_heap_sizer { double maximum_multiplier; double minimum_free_space; double expansiveness; - int stopping; - pthread_t thread; pthread_mutex_t lock; - pthread_cond_t cond; + int background_task_id; + uint64_t last_bytes_allocated; + uint64_t last_heartbeat; }; // With lock @@ -86,46 +87,31 @@ gc_adaptive_heap_sizer_on_gc(struct gc_adaptive_heap_sizer *sizer, pthread_mutex_unlock(&sizer->lock); } -static void* -gc_adaptive_heap_sizer_thread(void *data) { +static void +gc_adaptive_heap_sizer_background_task(void *data) { struct gc_adaptive_heap_sizer *sizer = data; - uint64_t last_bytes_allocated = + uint64_t bytes_allocated = sizer->get_allocation_counter(sizer->callback_data); - uint64_t last_heartbeat = gc_platform_monotonic_nanoseconds(); - pthread_mutex_lock(&sizer->lock); - while (!sizer->stopping) { - { - struct timespec ts; - if (clock_gettime(CLOCK_REALTIME, &ts)) { - perror("adaptive heap sizer thread: failed to get time!"); - break; - } - ts.tv_sec += 1; - pthread_cond_timedwait(&sizer->cond, &sizer->lock, &ts); - } - uint64_t bytes_allocated = - sizer->get_allocation_counter(sizer->callback_data); - uint64_t heartbeat = gc_platform_monotonic_nanoseconds(); - double rate = (double) (bytes_allocated - last_bytes_allocated) / - (double) (heartbeat - last_heartbeat); - // Just smooth the rate, under the assumption that the denominator is almost - // always 1. - sizer->smoothed_allocation_rate *= 1.0 - sizer->allocation_smoothing_factor; - sizer->smoothed_allocation_rate += rate * sizer->allocation_smoothing_factor; - last_heartbeat = heartbeat; - last_bytes_allocated = bytes_allocated; - sizer->set_heap_size(gc_adaptive_heap_sizer_calculate_size(sizer), - sizer->callback_data); - } + uint64_t heartbeat = gc_platform_monotonic_nanoseconds(); + double rate = (double) (bytes_allocated - sizer->last_bytes_allocated) / + (double) (heartbeat - sizer->last_heartbeat); + // Just smooth the rate, under the assumption that the denominator is almost + // always 1. + sizer->smoothed_allocation_rate *= 1.0 - sizer->allocation_smoothing_factor; + sizer->smoothed_allocation_rate += rate * sizer->allocation_smoothing_factor; + sizer->last_heartbeat = heartbeat; + sizer->last_bytes_allocated = bytes_allocated; + sizer->set_heap_size(gc_adaptive_heap_sizer_calculate_size(sizer), + sizer->callback_data); pthread_mutex_unlock(&sizer->lock); - return NULL; } static struct gc_adaptive_heap_sizer* gc_make_adaptive_heap_sizer(double expansiveness, uint64_t (*get_allocation_counter)(void *), void (*set_heap_size)(size_t , void *), - void *callback_data) { + void *callback_data, + struct gc_background_thread *thread) { struct gc_adaptive_heap_sizer *sizer; sizer = malloc(sizeof(*sizer)); if (!sizer) @@ -149,25 +135,15 @@ gc_make_adaptive_heap_sizer(double expansiveness, sizer->maximum_multiplier = 5; sizer->minimum_free_space = 4 * 1024 * 1024; sizer->expansiveness = expansiveness; - pthread_mutex_init(&sizer->lock, NULL); - pthread_cond_init(&sizer->cond, NULL); - if (pthread_create(&sizer->thread, NULL, gc_adaptive_heap_sizer_thread, - sizer)) { - perror("spawning adaptive heap size thread failed"); - GC_CRASH(); - } + pthread_mutex_init(&thread->lock, NULL); + sizer->last_bytes_allocated = get_allocation_counter(callback_data); + sizer->last_heartbeat = gc_platform_monotonic_nanoseconds(); + sizer->background_task_id = thread + ? gc_background_thread_add_task(thread, GC_BACKGROUND_TASK_FIRST, + gc_adaptive_heap_sizer_background_task, + sizer) + : -1; return sizer; } -static void -gc_destroy_adaptive_heap_sizer(struct gc_adaptive_heap_sizer *sizer) { - pthread_mutex_lock(&sizer->lock); - GC_ASSERT(!sizer->stopping); - sizer->stopping = 1; - pthread_mutex_unlock(&sizer->lock); - pthread_cond_signal(&sizer->cond); - pthread_join(sizer->thread, NULL); - free(sizer); -} - #endif // ADAPTIVE_HEAP_SIZER_H diff --git a/src/background-thread.h b/src/background-thread.h new file mode 100644 index 000000000..ee858ac58 --- /dev/null +++ b/src/background-thread.h @@ -0,0 +1,138 @@ +#ifndef BACKGROUND_THREAD_H +#define BACKGROUND_THREAD_H + +#include +#include +#include + +#include "assert.h" +#include "debug.h" + +enum { + GC_BACKGROUND_TASK_FIRST = 0, + GC_BACKGROUND_TASK_NORMAL = 100, + GC_BACKGROUND_TASK_LAST = 200 +}; + +struct gc_background_task { + int id; + int priority; + void (*run)(void *data); + void *data; +}; + +struct gc_background_thread { + size_t count; + size_t capacity; + struct gc_background_task *tasks; + int next_id; + int stopping; + pthread_t thread; + pthread_mutex_t lock; + pthread_cond_t cond; +}; + +static void* +gc_background_thread(void *data) { + struct gc_background_thread *thread = data; + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts)) { + perror("background thread: failed to get time!"); + return NULL; + } + pthread_mutex_lock(&thread->lock); + while (!thread->stopping) { + ts.tv_sec += 1; + pthread_cond_timedwait(&thread->cond, &thread->lock, &ts); + if (thread->stopping) + break; + for (size_t i = 0; i < thread->count; i++) + thread->tasks[i].run(thread->tasks[i].data); + } + pthread_mutex_unlock(&thread->lock); + return NULL; +} + +static struct gc_background_thread* +gc_make_background_thread(void) { + struct gc_background_thread *thread; + thread = malloc(sizeof(*thread)); + if (!thread) + GC_CRASH(); + memset(thread, 0, sizeof(*thread)); + thread->tasks = NULL; + thread->count = 0; + thread->capacity = 0; + pthread_mutex_init(&thread->lock, NULL); + pthread_cond_init(&thread->cond, NULL); + if (pthread_create(&thread->thread, NULL, gc_background_thread, thread)) { + perror("spawning background thread failed"); + GC_CRASH(); + } + return thread; +} + +static int +gc_background_thread_add_task(struct gc_background_thread *thread, + int priority, void (*run)(void *data), + void *data) { + pthread_mutex_lock(&thread->lock); + if (thread->count == thread->capacity) { + size_t new_capacity = thread->capacity * 2 + 1; + struct gc_background_task *new_tasks = + realloc(thread->tasks, sizeof(struct gc_background_task) * new_capacity); + if (!new_tasks) { + perror("ran out of space for background tasks!"); + GC_CRASH(); + } + thread->capacity = new_capacity; + thread->tasks = new_tasks; + } + size_t insert = 0; + for (; insert < thread->count; insert++) { + if (priority < thread->tasks[insert].priority) + break; + } + size_t bytes_to_move = + (thread->count - insert) * sizeof(struct gc_background_task); + memmove(&thread->tasks[insert + 1], &thread->tasks[insert], bytes_to_move); + int id = thread->next_id++; + thread->tasks[insert].id = id; + thread->tasks[insert].priority = priority; + thread->tasks[insert].run = run; + thread->tasks[insert].data = data; + thread->count++; + pthread_mutex_unlock(&thread->lock); + return id; +} + +static void +gc_background_thread_remove_task(struct gc_background_thread *thread, + int id) { + pthread_mutex_lock(&thread->lock); + size_t remove = 0; + for (; remove < thread->count; remove++) { + if (thread->tasks[remove].id == id) + break; + } + if (remove == thread->count) + GC_CRASH(); + size_t bytes_to_move = + (thread->count - (remove + 1)) * sizeof(struct gc_background_task); + memmove(&thread->tasks[remove], &thread->tasks[remove + 1], bytes_to_move); + pthread_mutex_unlock(&thread->lock); +} + +static void +gc_destroy_background_thread(struct gc_background_thread *thread) { + pthread_mutex_lock(&thread->lock); + GC_ASSERT(!thread->stopping); + thread->stopping = 1; + pthread_mutex_unlock(&thread->lock); + pthread_cond_signal(&thread->cond); + pthread_join(thread->thread, NULL); + free(thread->tasks); + free(thread); +} + +#endif // BACKGROUND_THREAD_H diff --git a/src/heap-sizer.h b/src/heap-sizer.h index dc6f3d2ef..eb038cca9 100644 --- a/src/heap-sizer.h +++ b/src/heap-sizer.h @@ -20,7 +20,8 @@ gc_make_heap_sizer(struct gc_heap *heap, const struct gc_common_options *options, uint64_t (*get_allocation_counter_from_thread)(void*), void (*set_heap_size_from_thread)(size_t, void*), - void *data) { + void *data, + struct gc_background_thread *thread) { struct gc_heap_sizer ret = { options->heap_size_policy, }; switch (options->heap_size_policy) { case GC_HEAP_SIZE_FIXED: @@ -35,7 +36,7 @@ gc_make_heap_sizer(struct gc_heap *heap, gc_make_adaptive_heap_sizer (options->heap_expansiveness, get_allocation_counter_from_thread, set_heap_size_from_thread, - heap); + heap, thread); break; default: diff --git a/src/mmc.c b/src/mmc.c index 34a96e409..061d3b80f 100644 --- a/src/mmc.c +++ b/src/mmc.c @@ -9,6 +9,7 @@ #define GC_IMPL 1 #include "gc-internal.h" +#include "background-thread.h" #include "debug.h" #include "gc-align.h" #include "gc-inline.h" @@ -58,6 +59,7 @@ struct gc_heap { double minimum_major_gc_yield_threshold; double pending_ephemerons_size_factor; double pending_ephemerons_size_slop; + struct gc_background_thread *background_thread; struct gc_heap_sizer sizer; struct gc_event_listener event_listener; void *event_listener_data; @@ -1071,10 +1073,12 @@ gc_init(const struct gc_options *options, struct gc_stack_addr *stack_base, if (!large_object_space_init(heap_large_object_space(*heap), *heap)) GC_CRASH(); + (*heap)->background_thread = gc_make_background_thread(); (*heap)->sizer = gc_make_heap_sizer(*heap, &options->common, allocation_counter_from_thread, set_heap_size_from_thread, - (*heap)); + (*heap), + (*heap)->background_thread); *mut = calloc(1, sizeof(struct gc_mutator)); if (!*mut) GC_CRASH(); diff --git a/src/pcc.c b/src/pcc.c index 40ba60f8b..2cd919e50 100644 --- a/src/pcc.c +++ b/src/pcc.c @@ -9,6 +9,7 @@ #define GC_IMPL 1 #include "gc-internal.h" +#include "background-thread.h" #include "copy-space.h" #include "debug.h" #include "gc-align.h" @@ -48,6 +49,7 @@ struct gc_heap { struct gc_tracer tracer; double pending_ephemerons_size_factor; double pending_ephemerons_size_slop; + struct gc_background_thread *background_thread; struct gc_heap_sizer sizer; struct gc_event_listener event_listener; void *event_listener_data; @@ -658,10 +660,12 @@ int gc_init(const struct gc_options *options, struct gc_stack_addr *stack_base, if (!large_object_space_init(heap_large_object_space(*heap), *heap)) GC_CRASH(); + (*heap)->background_thread = gc_make_background_thread(); (*heap)->sizer = gc_make_heap_sizer(*heap, &options->common, allocation_counter_from_thread, set_heap_size_from_thread, - (*heap)); + (*heap), + (*heap)->background_thread); *mut = calloc(1, sizeof(struct gc_mutator)); if (!*mut) GC_CRASH();