Created
October 21, 2008 03:16
-
-
Save rdp/18242 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: gc.c | |
=================================================================== | |
--- gc.c (revision 19984) | |
+++ gc.c (working copy) | |
@@ -281,7 +281,7 @@ | |
int limit; | |
}; | |
-#define HEAP_MIN_SLOTS 10000 | |
+#define HEAP_MIN_SLOTS 100000 | |
#define FREE_MIN 4096 | |
struct gc_list { | |
@@ -395,11 +395,45 @@ | |
/*#define HEAP_SIZE 0x800 */ | |
#define HEAP_OBJ_LIMIT (HEAP_SIZE / sizeof(struct RVALUE)) | |
+#define GC_NOTIFY 1 | |
extern st_table *rb_class_tbl; | |
int ruby_disable_gc_stress = 0; | |
+ | |
+ | |
+ | |
+#include <sys/types.h> | |
+#include <sys/mman.h> | |
+#include <fcntl.h> | |
+ | |
+#define TO_ADD_TO_FREE_LIST_SIZE 4000000 | |
+#define HEAPS_TO_FREE_SIZE 3000 | |
+enum CHILD_STATE { idle, child_in_gc, gc_just_finished, gc_wait_for_child_next_pass}; | |
+typedef struct | |
+{ | |
+ enum CHILD_STATE child_state; | |
+ RVALUE* to_add_to_free_list[TO_ADD_TO_FREE_LIST_SIZE]; | |
+ struct heaps_slot* heaps_to_free[HEAPS_TO_FREE_SIZE]; | |
+ | |
+} shared_stuff_t, *shared_stuff_p; | |
+ | |
+shared_stuff_p shared_area = 0; | |
+int child_gc_pid = 0; | |
+ | |
+/* TODO wait for the child pid on exit */ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
static void run_final(rb_objspace_t *objspace, VALUE obj); | |
static int garbage_collect(rb_objspace_t *objspace); | |
@@ -546,6 +580,7 @@ | |
if ((ruby_gc_stress && !ruby_disable_gc_stress) || | |
(malloc_increase+size) > malloc_limit) { | |
garbage_collect(objspace); | |
+ malloc_increase = 0; | |
} | |
mem = malloc(size); | |
if (!mem) { | |
@@ -889,9 +924,11 @@ | |
VALUE obj; | |
if ((ruby_gc_stress && !ruby_disable_gc_stress) || !freelist) { | |
- if (!heaps_increment(objspace) && !garbage_collect(objspace)) { | |
- during_gc = 0; | |
- rb_memerror(); | |
+ if (!heaps_increment(objspace)) { | |
+ if(!garbage_collect(objspace)) { | |
+ during_gc = 0; | |
+ rb_memerror(); | |
+ } | |
} | |
} | |
@@ -1621,95 +1658,59 @@ | |
static void | |
gc_sweep(rb_objspace_t *objspace) | |
{ | |
- RVALUE *p, *pend, *final_list; | |
- size_t freed = 0; | |
- size_t i; | |
- size_t live = 0, free_min = 0, do_heap_free = 0; | |
+ RVALUE *p, *pend; | |
+ size_t i; /* TODO were there more here we needed to worry about? */ | |
+ size_t live = 0; | |
+ int where_at_in_to_add_to_free_list = 0; | |
+ int heaps_to_free_where_at = 0; | |
+ int total_added_to_to_free_list; | |
- do_heap_free = (heaps_used * HEAP_OBJ_LIMIT) * 0.65; | |
- free_min = (heaps_used * HEAP_OBJ_LIMIT) * 0.2; | |
- | |
- if (free_min < FREE_MIN) { | |
- do_heap_free = heaps_used * HEAP_OBJ_LIMIT; | |
- free_min = FREE_MIN; | |
- } | |
- | |
- freelist = 0; | |
- final_list = deferred_final_list; | |
- deferred_final_list = 0; | |
for (i = 0; i < heaps_used; i++) { | |
- int free_num = 0, final_num = 0; | |
- RVALUE *free = freelist; | |
- RVALUE *final = final_list; | |
- int deferred; | |
+ int free_num = 0; | |
p = heaps[i].slot; pend = p + heaps[i].limit; | |
+ | |
+ total_added_to_to_free_list = 0; | |
while (p < pend) { | |
if (!(p->as.basic.flags & FL_MARK)) { | |
- if (p->as.basic.flags && | |
- ((deferred = obj_free(objspace, (VALUE)p)) || | |
- ((FL_TEST(p, FL_FINALIZE)) && need_call_final))) { | |
- if (!deferred) { | |
- p->as.free.flags = T_ZOMBIE; | |
- RDATA(p)->dfree = 0; | |
+ if (p->as.basic.flags) { | |
+ free_num++; /* for now only count those that we honestly free this time */ | |
+ total_added_to_to_free_list++; | |
+ if(where_at_in_to_add_to_free_list < TO_ADD_TO_FREE_LIST_SIZE - 2) | |
+ { | |
+ shared_area->to_add_to_free_list[where_at_in_to_add_to_free_list++] = p; | |
} | |
- p->as.free.flags |= FL_MARK; | |
- p->as.free.next = final_list; | |
- final_list = p; | |
- final_num++; | |
+ | |
} | |
- else { | |
- add_freelist(objspace, p); | |
- free_num++; | |
- } | |
} | |
- else if (BUILTIN_TYPE(p) == T_ZOMBIE) { | |
+ else if (BUILTIN_TYPE(p) == T_ZOMBIE) { | |
/* objects to be finalized */ | |
/* do nothing remain marked */ | |
} | |
else { | |
- RBASIC(p)->flags &= ~FL_MARK; | |
- live++; | |
+ //live++; | |
} | |
p++; | |
} | |
- if (final_num + free_num == heaps[i].limit && freed > do_heap_free) { | |
- RVALUE *pp; | |
+ | |
+ if (free_num == heaps[i].limit) { | |
+ if(heaps_to_free_where_at < (HEAPS_TO_FREE_SIZE - 2)) | |
+ { | |
+ /* TODO do not list them on the to be freed list or they will be added to the freelist when collected */ | |
+ //printf("marking heap to be totally freed %d\n", heaps[i].membase); /* this has the right number*/ | |
+ shared_area->heaps_to_free[heaps_to_free_where_at++] = heaps[i].membase; /* TODO do we still add these to the to free list though? */ | |
+ } | |
+ } | |
+ } /* foreach heap */ | |
- for (pp = final_list; pp != final; pp = pp->as.free.next) { | |
- RDATA(pp)->dmark = (void *)&heaps[i]; | |
- pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */ | |
- } | |
- heaps[i].limit = final_num; | |
+ if(where_at_in_to_add_to_free_list == TO_ADD_TO_FREE_LIST_SIZE - 2) | |
+ if (GC_NOTIFY) printf("PROBABLY WASTED free list size wanted %d, size is %d\n", where_at_in_to_add_to_free_list, TO_ADD_TO_FREE_LIST_SIZE); | |
- freelist = free; /* cancel this page from freelist */ | |
- } | |
- else { | |
- freed += free_num; | |
- } | |
- } | |
- GC_PROF_SET_MALLOC_INFO; | |
- if (malloc_increase > malloc_limit) { | |
- malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed); | |
- if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT; | |
- } | |
- malloc_increase = 0; | |
- if (freed < free_min) { | |
- set_heaps_increment(objspace); | |
- heaps_increment(objspace); | |
- } | |
- during_gc = 0; | |
- | |
- /* clear finalization list */ | |
- if (final_list) { | |
- GC_PROF_SET_HEAP_INFO; | |
- deferred_final_list = final_list; | |
- RUBY_VM_SET_FINALIZER_INTERRUPT(GET_THREAD()); | |
- } | |
- else{ | |
- free_unused_heaps(objspace); | |
- GC_PROF_SET_HEAP_INFO; | |
- } | |
+ /* add some null terminators */ | |
+ shared_area->to_add_to_free_list[where_at_in_to_add_to_free_list] = 0; | |
+ shared_area->heaps_to_free[heaps_to_free_where_at] = 0; | |
+ | |
+ return; | |
} | |
void | |
@@ -1855,7 +1856,6 @@ | |
return 0; | |
} | |
-#define GC_NOTIFY 0 | |
void rb_vm_mark(void *ptr); | |
@@ -1902,9 +1902,178 @@ | |
void rb_gc_mark_encodings(void); | |
+ | |
+static void | |
+gc_free_from_child() | |
+{ | |
+ RVALUE *p; | |
+ int where_at_in_to_add_to_free_list = 0; | |
+ int deferred; | |
+ rb_objspace_t *objspace = &rb_objspace; | |
+ p = shared_area->to_add_to_free_list[0]; | |
+ while(p != 0) { | |
+ if( (deferred = obj_free(objspace, (VALUE)p)) || | |
+ ((FL_TEST(p, FL_FINALIZE)) && need_call_final)) | |
+ { | |
+ if (!deferred) { | |
+ p->as.free.flags = T_ZOMBIE; | |
+ RDATA(p)->dfree = 0; | |
+ } | |
+ p->as.free.flags |= FL_MARK; | |
+ p->as.free.next = deferred_final_list; | |
+ deferred_final_list = p; | |
+ }else | |
+ add_freelist(objspace, (RVALUE *)p); | |
+ | |
+ p = shared_area->to_add_to_free_list[++where_at_in_to_add_to_free_list]; | |
+ } | |
+ | |
+ /* free heaps -- TODO | |
+ if(shared_area->heaps_to_free[0]) | |
+ { | |
+ struct heaps_slot *a = shared_area->heaps_to_free[0]; | |
+ int here = 0; | |
+ struct heaps_slot *p2 = shared_area->heaps_to_free[here]; | |
+ while(p2) | |
+ { | |
+ p2 = shared_area->heaps_to_free[++here]; | |
+ //p2->limit = 0; | |
+ } | |
+ //free_unused_heaps(objspace); | |
+ } */ | |
+ | |
+} | |
+ | |
+static void init_shared_memory() { | |
+#ifndef _DARWIN_C_SOURCE | |
+ int fd; | |
+#endif | |
+ if( child_gc_pid) // todo take out | |
+ { | |
+ printf("ERROR CHILD IS %d -- which means it was initialized twice! unexpected\n", child_gc_pid); | |
+ _exit(EXIT_FAILURE); | |
+ } | |
+ | |
+#ifdef _DARWIN_C_SOURCE | |
+ shared_area=(shared_stuff_p)mmap(0, | |
+ sizeof(shared_stuff_t), | |
+ PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON, | |
+ -1, | |
+ 0 | |
+ ); | |
+#else | |
+ fd=open("/dev/zero", | |
+ O_RDWR | |
+ ); | |
+ | |
+ if(fd==-1) | |
+ { | |
+ printf("unable to get file handle for attempted shared memory--exiting!\n"); | |
+ _exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ shared_area=(shared_stuff_p)mmap(0, | |
+ sizeof(shared_stuff_t), | |
+ PROT_READ|PROT_WRITE,MAP_SHARED, | |
+ fd, | |
+ 0 | |
+ ); | |
+#endif | |
+ | |
+ if(shared_area==(shared_stuff_p)-1) | |
+ { | |
+ printf("shared memory allocation failure! %s\n", strerror(errno)); | |
+ _exit(EXIT_FAILURE); | |
+ } | |
+ shared_area->child_state = idle; | |
+} | |
+ | |
static int | |
garbage_collect(rb_objspace_t *objspace) | |
{ | |
+ pid_t w; | |
+ int status; | |
+ | |
+ if(shared_area==0){ | |
+ init_shared_memory(); | |
+ } | |
+ | |
+ int saved_child_state = shared_area->child_state; | |
+ if (dont_gc || during_gc || (saved_child_state == child_in_gc)) { | |
+ | |
+ // we should add if we're dont_gc or during_gc and !freelist, else wait for child | |
+ if(!freelist && (dont_gc || during_gc)) | |
+ { | |
+ set_heaps_increment(objspace); | |
+ heaps_increment(objspace); | |
+ return Qtrue; | |
+ } else | |
+ { | |
+ // we should wait - cause waitpid to be called below | |
+ saved_child_state = gc_wait_for_child_next_pass; | |
+ } | |
+ } | |
+ | |
+ | |
+ if(saved_child_state == gc_just_finished || saved_child_state == gc_wait_for_child_next_pass) | |
+ { | |
+ during_gc++; | |
+ if(GC_NOTIFY) | |
+ if(saved_child_state == gc_just_finished) | |
+ printf("receiving WELL a child collecting thread\n"); | |
+ else | |
+ printf("receiving POORLY a child collecting thread \n"); | |
+ // reap child process | |
+ w = waitpid(child_gc_pid, &status, 0); | |
+ child_gc_pid = 0; | |
+ if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); } | |
+ gc_free_from_child(); | |
+ if (!freelist) { | |
+ // our collecting from child was unsuccessful | |
+ if (!heaps_increment(objspace)) { | |
+ set_heaps_increment(objspace); | |
+ heaps_increment(objspace); | |
+ } | |
+ | |
+ } | |
+ shared_area->child_state = idle; | |
+ during_gc = 0; | |
+ return Qtrue; | |
+ } | |
+ | |
+ | |
+ if(shared_area->child_state == child_in_gc) | |
+ { | |
+ printf("attempted to start two children on accident: bug\n"); | |
+ raise(SIGTERM); | |
+ } | |
+ | |
+ /* now the case of there was no child running - start the child */ | |
+ if (GC_NOTIFY) fflush(stdout); | |
+ if(child_gc_pid) | |
+ { | |
+ printf("err -- looks like child exists already\n"); | |
+ raise(SIGTERM); | |
+ } | |
+ shared_area->child_state = child_in_gc; | |
+ int childs_pid = fork(); | |
+ if(childs_pid != 0) | |
+ { | |
+ // parent: | |
+ child_gc_pid = childs_pid; | |
+ if (!freelist) { | |
+ if (!heaps_increment(objspace)) { | |
+ /* At this point we can either add more [and possibly grow forever] or | |
+ we can call GC again and wait on our newly created child thread */ | |
+ return garbage_collect(objspace); | |
+ } | |
+ | |
+ } | |
+ return Qtrue; | |
+ | |
+ } | |
+ | |
+ // child: | |
struct gc_list *list; | |
rb_thread_t *th = GET_THREAD(); | |
INIT_GC_PROF_PARAMS; | |
@@ -1915,15 +2084,6 @@ | |
return Qfalse; | |
} | |
- if (dont_gc || during_gc) { | |
- if (!freelist) { | |
- if (!heaps_increment(objspace)) { | |
- set_heaps_increment(objspace); | |
- heaps_increment(objspace); | |
- } | |
- } | |
- return Qtrue; | |
- } | |
during_gc++; | |
objspace->count++; | |
@@ -1976,7 +2136,10 @@ | |
GC_PROF_TIMER_STOP; | |
if (GC_NOTIFY) printf("end garbage_collect()\n"); | |
- return Qtrue; | |
+ | |
+ shared_area->child_state = gc_just_finished; | |
+ _exit(EXIT_SUCCESS); | |
+ return Qtrue; // we never get here | |
} | |
int | |
@@ -2376,6 +2539,8 @@ | |
rb_gc(void) | |
{ | |
rb_objspace_t *objspace = &rb_objspace; | |
+ if(shared_area && (shared_area->child_state == child_in_gc)) | |
+ shared_area->child_state = gc_wait_for_child_next_pass; // allow two calls to GC.start to force a GC | |
garbage_collect(objspace); | |
gc_finalize_deferred(objspace); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment