mirror of
https://github.com/justinethier/cyclone.git
synced 2025-07-03 11:16:34 +02:00
Finish heap merging, use separate heap locks
This commit is contained in:
parent
e30ff16b3d
commit
8fa1bccd78
3 changed files with 65 additions and 42 deletions
|
@ -35,15 +35,15 @@
|
||||||
(string-append name ":" s1 ":" s2 ":" s3 ":" s4)
|
(string-append name ":" s1 ":" s2 ":" s3 ":" s4)
|
||||||
count
|
count
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(thread-start!
|
#;(thread-start!
|
||||||
(make-thread
|
(make-thread
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
||||||
(thread-start!
|
#;(thread-start!
|
||||||
(make-thread
|
(make-thread
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
||||||
(thread-start!
|
#;(thread-start!
|
||||||
(make-thread
|
(make-thread
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
||||||
|
@ -51,6 +51,7 @@
|
||||||
(make-thread
|
(make-thread
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
(ctak (hide count input1) (hide count input2) (hide count input3)))))
|
||||||
|
(thread-sleep! 10000)
|
||||||
(ctak (hide count input1) (hide count input2) (hide count input3))
|
(ctak (hide count input1) (hide count input2) (hide count input3))
|
||||||
|
|
||||||
;; TODO: thread-join
|
;; TODO: thread-join
|
||||||
|
|
85
gc.c
85
gc.c
|
@ -55,14 +55,11 @@ static void **mark_stack = NULL;
|
||||||
static int mark_stack_len = 0;
|
static int mark_stack_len = 0;
|
||||||
static int mark_stack_i = 0;
|
static int mark_stack_i = 0;
|
||||||
|
|
||||||
// OBSOLETE:
|
// Data for the "main" thread which is guaranteed to always be there.
|
||||||
//// Lock to protect the heap from concurrent modifications
|
// Per SRFI 18:
|
||||||
//static pthread_mutex_t heap_lock;
|
// All threads are terminated when the primordial
|
||||||
//
|
// thread terminates (normally or not).
|
||||||
//// Cached heap statistics
|
static gc_thread_data *primordial_thread = NULL;
|
||||||
//static uint64_t cached_heap_free_sizes[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
//static uint64_t cached_heap_total_sizes[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
//// END OBSOLETE
|
|
||||||
|
|
||||||
// Data for each individual mutator thread
|
// Data for each individual mutator thread
|
||||||
ck_array_t Cyc_mutators, old_mutators;
|
ck_array_t Cyc_mutators, old_mutators;
|
||||||
|
@ -162,6 +159,11 @@ void gc_add_mutator(gc_thread_data * thd)
|
||||||
}
|
}
|
||||||
ck_array_commit(&Cyc_mutators);
|
ck_array_commit(&Cyc_mutators);
|
||||||
pthread_mutex_unlock(&mutators_lock);
|
pthread_mutex_unlock(&mutators_lock);
|
||||||
|
|
||||||
|
// Main thread is always the first one added
|
||||||
|
if (primordial_thread == NULL) {
|
||||||
|
primordial_thread = thd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove selected mutator from the mutator list.
|
// Remove selected mutator from the mutator list.
|
||||||
|
@ -479,7 +481,7 @@ int gc_grow_heap(gc_heap * h, int heap_type, size_t size, size_t chunk_size, gc_
|
||||||
{
|
{
|
||||||
size_t /*cur_size,*/ new_size;
|
size_t /*cur_size,*/ new_size;
|
||||||
gc_heap *h_last = h, *h_new;
|
gc_heap *h_last = h, *h_new;
|
||||||
pthread_mutex_lock(&(thd->lock)); //heap_lock);
|
pthread_mutex_lock(&(thd->heap_lock));
|
||||||
// Compute size of new heap page
|
// Compute size of new heap page
|
||||||
if (heap_type == HEAP_HUGE) {
|
if (heap_type == HEAP_HUGE) {
|
||||||
new_size = gc_heap_align(size) + 128;
|
new_size = gc_heap_align(size) + 128;
|
||||||
|
@ -522,7 +524,7 @@ int gc_grow_heap(gc_heap * h, int heap_type, size_t size, size_t chunk_size, gc_
|
||||||
// Done with computing new page size
|
// Done with computing new page size
|
||||||
h_new = gc_heap_create(heap_type, new_size, h_last->max_size, chunk_size, thd);
|
h_new = gc_heap_create(heap_type, new_size, h_last->max_size, chunk_size, thd);
|
||||||
h_last->next = h_new;
|
h_last->next = h_new;
|
||||||
pthread_mutex_unlock(&(thd->lock)); //heap_lock);
|
pthread_mutex_unlock(&(thd->heap_lock));
|
||||||
#if GC_DEBUG_TRACE
|
#if GC_DEBUG_TRACE
|
||||||
fprintf(stderr, "DEBUG - grew heap\n");
|
fprintf(stderr, "DEBUG - grew heap\n");
|
||||||
#endif
|
#endif
|
||||||
|
@ -534,7 +536,7 @@ void *gc_try_alloc(gc_heap * h, int heap_type, size_t size, char *obj,
|
||||||
{
|
{
|
||||||
gc_heap *h_passed = h;
|
gc_heap *h_passed = h;
|
||||||
gc_free_list *f1, *f2, *f3;
|
gc_free_list *f1, *f2, *f3;
|
||||||
pthread_mutex_lock(&(thd->lock)); //heap_lock);
|
pthread_mutex_lock(&(thd->heap_lock));
|
||||||
// Start searching from the last heap page we had a successful
|
// Start searching from the last heap page we had a successful
|
||||||
// allocation from, unless the current request is for a smaller
|
// allocation from, unless the current request is for a smaller
|
||||||
// block in which case there may be available memory closer to
|
// block in which case there may be available memory closer to
|
||||||
|
@ -566,12 +568,12 @@ void *gc_try_alloc(gc_heap * h, int heap_type, size_t size, char *obj,
|
||||||
}
|
}
|
||||||
h_passed->next_free = h;
|
h_passed->next_free = h;
|
||||||
h_passed->last_alloc_size = size;
|
h_passed->last_alloc_size = size;
|
||||||
pthread_mutex_unlock(&(thd->lock)); //heap_lock);
|
pthread_mutex_unlock(&(thd->heap_lock));
|
||||||
return f2;
|
return f2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&(thd->lock)); //heap_lock);
|
pthread_mutex_unlock(&(thd->heap_lock));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,9 +619,9 @@ void *gc_alloc(gc_heap_root * hrt, size_t size, char *obj, gc_thread_data * thd,
|
||||||
if (!result) {
|
if (!result) {
|
||||||
fprintf(stderr, "out of memory error allocating %zu bytes\n", size);
|
fprintf(stderr, "out of memory error allocating %zu bytes\n", size);
|
||||||
fprintf(stderr, "Heap type %d diagnostics:\n", heap_type);
|
fprintf(stderr, "Heap type %d diagnostics:\n", heap_type);
|
||||||
pthread_mutex_lock(&(thd->lock)); //heap_lock);
|
pthread_mutex_lock(&(thd->heap_lock));
|
||||||
gc_print_stats(h);
|
gc_print_stats(h);
|
||||||
pthread_mutex_unlock(&(thd->lock)); //heap_lock); // why not
|
pthread_mutex_unlock(&(thd->heap_lock)); // why not
|
||||||
exit(1); // could throw error, but OOM is a major issue, so...
|
exit(1); // could throw error, but OOM is a major issue, so...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -798,13 +800,9 @@ size_t gc_sweep(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_thread_da
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lock the heap to prevent issues with allocations during sweep
|
// Lock the heap to prevent issues with allocations during sweep
|
||||||
// It sucks to have to use a coarse-grained lock like this, but let's
|
// This coarse-grained lock actually performed better than a fine-grained one.
|
||||||
// be safe and prevent threading issues right now. Once the new GC
|
|
||||||
// works we can go back and try to speed things up (if possible)
|
|
||||||
// by using more fine-grained locking. Can also profile to see
|
|
||||||
// how much time is even spent sweeping
|
|
||||||
//
|
//
|
||||||
pthread_mutex_lock(&(thd->lock)); //heap_lock);
|
pthread_mutex_lock(&(thd->heap_lock));
|
||||||
h->next_free = h;
|
h->next_free = h;
|
||||||
h->last_alloc_size = 0;
|
h->last_alloc_size = 0;
|
||||||
|
|
||||||
|
@ -945,7 +943,7 @@ size_t gc_sweep(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_thread_da
|
||||||
gc_print_stats(orig_heap_ptr);
|
gc_print_stats(orig_heap_ptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pthread_mutex_unlock(&(thd->lock)); //heap_lock);
|
pthread_mutex_unlock(&(thd->heap_lock));
|
||||||
if (sum_freed_ptr)
|
if (sum_freed_ptr)
|
||||||
*sum_freed_ptr = sum_freed;
|
*sum_freed_ptr = sum_freed;
|
||||||
return max_freed;
|
return max_freed;
|
||||||
|
@ -1663,6 +1661,10 @@ void gc_thread_data_init(gc_thread_data * thd, int mut_num, char *stack_base,
|
||||||
thd->mark_buffer_len = 128;
|
thd->mark_buffer_len = 128;
|
||||||
thd->mark_buffer =
|
thd->mark_buffer =
|
||||||
vpbuffer_realloc(thd->mark_buffer, &(thd->mark_buffer_len));
|
vpbuffer_realloc(thd->mark_buffer, &(thd->mark_buffer_len));
|
||||||
|
if (pthread_mutex_init(&(thd->heap_lock), NULL) != 0) {
|
||||||
|
fprintf(stderr, "Unable to initialize thread mutex\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if (pthread_mutex_init(&(thd->lock), NULL) != 0) {
|
if (pthread_mutex_init(&(thd->lock), NULL) != 0) {
|
||||||
fprintf(stderr, "Unable to initialize thread mutex\n");
|
fprintf(stderr, "Unable to initialize thread mutex\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -1683,13 +1685,6 @@ void gc_thread_data_init(gc_thread_data * thd, int mut_num, char *stack_base,
|
||||||
void gc_thread_data_free(gc_thread_data * thd)
|
void gc_thread_data_free(gc_thread_data * thd)
|
||||||
{
|
{
|
||||||
if (thd) {
|
if (thd) {
|
||||||
//
|
|
||||||
// !!
|
|
||||||
// TODO: (not necessarily here, but somewhere need to roll heap pages into
|
|
||||||
// another thread data. need to include cached heap sizes/total, too.
|
|
||||||
// then free cached heap vars here.
|
|
||||||
// !!
|
|
||||||
//
|
|
||||||
if (pthread_mutex_destroy(&thd->lock) != 0) {
|
if (pthread_mutex_destroy(&thd->lock) != 0) {
|
||||||
// TODO: can only destroy the lock if it is unlocked. need to make sure we
|
// TODO: can only destroy the lock if it is unlocked. need to make sure we
|
||||||
// can guarantee that is the case prior to making this call
|
// can guarantee that is the case prior to making this call
|
||||||
|
@ -1697,6 +1692,23 @@ void gc_thread_data_free(gc_thread_data * thd)
|
||||||
fprintf(stderr, "Thread mutex is locked, unable to free\n");
|
fprintf(stderr, "Thread mutex is locked, unable to free\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (pthread_mutex_destroy(&thd->heap_lock) != 0) {
|
||||||
|
fprintf(stderr, "Thread heap mutex is locked, unable to free\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Merge heaps for the terminating thread into the main thread's heap.
|
||||||
|
// Eventually any data that is unused will be freed, but we need to
|
||||||
|
// keep the heap pages for now because they could still contain live
|
||||||
|
// objects.
|
||||||
|
// Lock the primordial thread (hopefully will not cause any deadlocks)
|
||||||
|
// but don't bother locking thd since it is already done by now.
|
||||||
|
pthread_mutex_lock(&(primordial_thread->heap_lock));
|
||||||
|
gc_merge_all_heaps(primordial_thread, thd);
|
||||||
|
pthread_mutex_unlock(&(primordial_thread->heap_lock));
|
||||||
|
if (thd->cached_heap_free_sizes)
|
||||||
|
free(thd->cached_heap_free_sizes);
|
||||||
|
if (thd->cached_heap_total_sizes);
|
||||||
|
free(thd->cached_heap_total_sizes);
|
||||||
if (thd->jmp_start)
|
if (thd->jmp_start)
|
||||||
free(thd->jmp_start);
|
free(thd->jmp_start);
|
||||||
if (thd->gc_args)
|
if (thd->gc_args)
|
||||||
|
@ -1723,18 +1735,27 @@ void gc_heap_merge(gc_heap *hdest, gc_heap *hsrc)
|
||||||
last->next = hsrc;
|
last->next = hsrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc_heap_merge_all(gc_heap_root *dest, gc_heap_root *src)
|
/**
|
||||||
|
* Merge all thread heaps into another.
|
||||||
|
* Assumes appropriate locks are already held.
|
||||||
|
*/
|
||||||
|
void gc_merge_all_heaps(gc_thread_data *dest, gc_thread_data *src)
|
||||||
{
|
{
|
||||||
gc_heap *hdest, *hsrc;
|
gc_heap *hdest, *hsrc;
|
||||||
int heap_type;
|
int heap_type;
|
||||||
|
|
||||||
for (heap_type = 0; heap_type < NUM_HEAP_TYPES; heap_type++) {
|
for (heap_type = 0; heap_type < NUM_HEAP_TYPES; heap_type++) {
|
||||||
hdest = dest->heap[heap_type];
|
hdest = dest->heap->heap[heap_type];
|
||||||
hsrc = src->heap[heap_type];
|
hsrc = src->heap->heap[heap_type];
|
||||||
if (hdest && hsrc) {
|
if (hdest && hsrc) {
|
||||||
gc_heap_merge(hdest, hsrc);
|
gc_heap_merge(hdest, hsrc);
|
||||||
|
ck_pr_add_64(&(dest->cached_heap_total_sizes[heap_type]),
|
||||||
|
ck_pr_load_64(&(src->cached_heap_total_sizes[heap_type])));
|
||||||
|
ck_pr_add_64(&(dest->cached_heap_free_sizes[heap_type]),
|
||||||
|
ck_pr_load_64(&(src->cached_heap_free_sizes[heap_type])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "Finished merging old heap data\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -221,6 +221,7 @@ struct gc_thread_data_t {
|
||||||
void **mark_buffer;
|
void **mark_buffer;
|
||||||
int mark_buffer_len;
|
int mark_buffer_len;
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
|
pthread_mutex_t heap_lock;
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
gc_heap_root *heap;
|
gc_heap_root *heap;
|
||||||
uint64_t *cached_heap_free_sizes;
|
uint64_t *cached_heap_free_sizes;
|
||||||
|
@ -663,7 +664,7 @@ gc_heap *gc_heap_create(int heap_type, size_t size, size_t max_size,
|
||||||
size_t chunk_size, gc_thread_data *thd);
|
size_t chunk_size, gc_thread_data *thd);
|
||||||
gc_heap *gc_heap_free(gc_heap *page, gc_heap *prev_page);
|
gc_heap *gc_heap_free(gc_heap *page, gc_heap *prev_page);
|
||||||
void gc_heap_merge(gc_heap *hdest, gc_heap *hsrc);
|
void gc_heap_merge(gc_heap *hdest, gc_heap *hsrc);
|
||||||
void gc_heap_merge_all(gc_heap_root *dest, gc_heap_root *src);
|
void gc_merge_all_heaps(gc_thread_data *dest, gc_thread_data *src);
|
||||||
void gc_print_stats(gc_heap * h);
|
void gc_print_stats(gc_heap * h);
|
||||||
int gc_grow_heap(gc_heap * h, int heap_type, size_t size, size_t chunk_size, gc_thread_data *thd);
|
int gc_grow_heap(gc_heap * h, int heap_type, size_t size, size_t chunk_size, gc_thread_data *thd);
|
||||||
char *gc_copy_obj(object hp, char *obj, gc_thread_data * thd);
|
char *gc_copy_obj(object hp, char *obj, gc_thread_data * thd);
|
||||||
|
|
Loading…
Add table
Reference in a new issue