Skip to content

Instantly share code, notes, and snippets.

@rdp
Created October 21, 2008 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdp/18242 to your computer and use it in GitHub Desktop.
Save rdp/18242 to your computer and use it in GitHub Desktop.
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