|
|
|
|
|
|
#include "global.h" |
|
#ifdef GC2 |
|
struct callback *gc_evaluator_callback=0; |
|
#include "array.h" |
#include "multiset.h" |
#include "mapping.h" |
#include "object.h" |
#include "program.h" |
#include "stralloc.h" |
#include "stuff.h" |
#include "error.h" |
#include "pike_memory.h" |
#include "pike_macros.h" |
#include "pike_types.h" |
|
#include "gc.h" |
#include "main.h" |
|
|
|
|
|
|
#define GC_CONST 20 |
#define MIN_ALLOC_THRESHOLD 1000 |
#define MAX_ALLOC_THRESHOLD 10000000 |
#define MULTIPLIER 0.9 |
#define MARKER_CHUNK_SIZE 1023 |
|
INT32 num_objects; |
INT32 num_allocs; |
INT32 alloc_threshold = MIN_ALLOC_THRESHOLD; |
|
static double objects_alloced; |
static double objects_freed; |
|
struct callback_list gc_callbacks; |
|
struct callback *add_gc_callback(callback_func call, |
void *arg, |
callback_func free_func) |
{ |
return add_to_callback(&gc_callbacks, call, arg, free_func); |
} |
|
#define GC_REFERENCED 1 |
#define GC_XREFERENCED 2 |
|
struct marker |
{ |
INT32 refs; |
#ifdef DEBUG |
INT32 xrefs; |
#endif |
INT32 flags; |
struct marker *next; |
void *marked; |
}; |
|
struct marker_chunk |
{ |
struct marker_chunk *next; |
struct marker markers[MARKER_CHUNK_SIZE]; |
}; |
|
static struct marker_chunk *chunk=0; |
static int markers_left_in_chunk=0; |
|
static struct marker *new_marker(void) |
{ |
if(!markers_left_in_chunk) |
{ |
struct marker_chunk *m; |
m=(struct marker_chunk *)xalloc(sizeof(struct marker_chunk)); |
m->next=chunk; |
chunk=m; |
markers_left_in_chunk=MARKER_CHUNK_SIZE; |
} |
markers_left_in_chunk--; |
|
return chunk->markers + markers_left_in_chunk; |
} |
|
static struct marker **hash=0; |
static unsigned long hashsize=0; |
|
static struct marker *getmark(void *a) |
{ |
int hashval; |
struct marker *m; |
|
hashval=((unsigned long)a)%hashsize; |
|
for(m=hash[hashval];m;m=m->next) |
if(m->marked == a) |
return m; |
|
m=new_marker(); |
m->marked=a; |
m->refs=0; |
#ifdef DEBUG |
m->xrefs=0; |
#endif |
m->flags=0; |
m->next=hash[hashval]; |
hash[hashval]=m; |
|
return m; |
} |
|
#ifdef DEBUG |
|
TYPE_T attempt_to_identify(void *something) |
{ |
struct array *a; |
struct object *o; |
struct program *p; |
|
a=&empty_array; |
do |
{ |
if(a==(struct array *)something) return T_ARRAY; |
a=a->next; |
}while(a!=&empty_array); |
|
for(o=first_object;o;o=o->next) |
if(o==(struct object *)something) |
return T_OBJECT; |
|
for(p=first_program;p;p=p->next) |
if(p==(struct program *)something) |
return T_PROGRAM; |
|
return T_UNKNOWN; |
} |
|
static void *check_for =0; |
static char *found_where=""; |
static void *found_in=0; |
static TYPE_T found_in_type=0; |
void *gc_svalue_location=0; |
|
static void gdb_gc_stop_here(void *a) |
{ |
fprintf(stderr,"**One ref found%s.\n",found_where); |
fprintf(stderr,"**Location of (short) svalue: %p\n",gc_svalue_location); |
describe_something(found_in, found_in_type); |
} |
|
TYPE_FIELD debug_gc_check_svalues(struct svalue *s, int num, TYPE_T t, void *data) |
{ |
TYPE_FIELD ret; |
found_in=data; |
found_in_type=t; |
ret=gc_check_svalues(s,num); |
found_in_type=T_UNKNOWN; |
return ret; |
} |
|
void debug_gc_check_short_svalue(union anything *u, TYPE_T type, TYPE_T t, void *data) |
{ |
found_in=data; |
found_in_type=t; |
gc_check_short_svalue(u,type); |
found_in_type=T_UNKNOWN; |
} |
|
void describe_something(void *a, TYPE_T t) |
{ |
struct program *p=(struct program *)a; |
if(!a) return; |
fprintf(stderr,"**Location: %p Type: %s Refs: %d\n",a, |
get_name_of_type(t), |
*(INT32 *)a); |
|
switch(t) |
{ |
case T_OBJECT: |
p=((struct object *)a)->prog; |
if(!p) |
{ |
fprintf(stderr,"**The object is destructed.\n"); |
break; |
} |
fprintf(stderr,"**Attempting to describe program object was instantiated from:\n"); |
|
case T_PROGRAM: |
if(!p->num_linenumbers) |
{ |
fprintf(stderr,"**The program was written in C.\n"); |
break; |
} |
if(p->linenumbers[0]==127) |
{ |
fprintf(stderr,"**The program may have been compiled from %s.\n",p->linenumbers+1); |
break; |
} |
fprintf(stderr,"**No information available about this program.\n"); |
break; |
|
case T_ARRAY: |
fprintf(stderr,"**Describing array:\n"); |
debug_dump_array((struct array *)a); |
break; |
} |
} |
|
#endif |
|
INT32 gc_check(void *a) |
{ |
#ifdef DEBUG |
if(check_for) |
{ |
if(check_for == a) |
{ |
gdb_gc_stop_here(a); |
} |
return 0; |
} |
#endif |
return getmark(a)->refs++; |
} |
|
int gc_is_referenced(void *a) |
{ |
struct marker *m; |
m=getmark(a); |
#ifdef DEBUG |
if(m->refs + m->xrefs > *(INT32 *)a) |
{ |
INT32 refs=m->refs; |
INT32 xrefs=m->xrefs; |
TYPE_T t=attempt_to_identify(a); |
|
fprintf(stderr,"**Something has %ld references, while gc() found %ld + %ld external.\n",(long)*(INT32 *)a,(long)refs,(long)xrefs); |
describe_something(t, a); |
|
fprintf(stderr,"**Looking for references:\n"); |
check_for=a; |
|
found_where=" in an array"; |
gc_check_all_arrays(); |
|
found_where=" in a multiset"; |
gc_check_all_multisets(); |
|
found_where=" in a mapping"; |
gc_check_all_mappings(); |
|
found_where=" in a program"; |
gc_check_all_programs(); |
|
found_where=" in an object"; |
gc_check_all_objects(); |
|
found_where=" in a module"; |
call_callback(& gc_callbacks, (void *)0); |
|
found_where=""; |
check_for=0; |
|
fatal("Ref counts are wrong (has %d, found %d + %d external)\n", |
*(INT32 *)a, |
refs, |
xrefs); |
} |
#endif |
return m->refs < *(INT32 *)a; |
} |
|
#ifdef DEBUG |
int gc_external_mark(void *a) |
{ |
struct marker *m; |
if(check_for) |
{ |
if(a==check_for) |
{ |
char *tmp=found_where; |
found_where="externally"; |
gdb_gc_stop_here(a); |
found_where=tmp; |
|
return 1; |
} |
return 0; |
} |
m=getmark(a); |
m->xrefs++; |
m->flags|=GC_XREFERENCED; |
gc_is_referenced(a); |
return 0; |
} |
#endif |
|
int gc_mark(void *a) |
{ |
struct marker *m; |
m=getmark(a); |
|
if(m->flags & GC_REFERENCED) |
{ |
return 0; |
}else{ |
m->flags |= GC_REFERENCED; |
return 1; |
} |
} |
|
int gc_do_free(void *a) |
{ |
struct marker *m; |
m=getmark(a); |
return !(m->flags & GC_REFERENCED); |
} |
|
|
static INT32 hashprimes[] = |
{ |
31, |
31, |
31, |
31, |
31, |
31, |
61, |
127, |
251, |
541, |
1151, |
2111, |
4327, |
8803, |
17903, |
32321, |
65599, |
133153, |
270001, |
547453, |
1109891, |
2000143, |
4561877, |
9248339, |
16777215, |
33554431, |
67108863, |
134217727, |
268435455, |
536870911, |
1073741823, |
2147483647, |
}; |
|
void do_gc(void) |
{ |
static int in_gc = 0; |
double tmp; |
INT32 tmp2,tmp3; |
struct marker_chunk *m; |
|
if(in_gc) return; |
in_gc=1; |
|
if(gc_evaluator_callback) |
{ |
remove_callback(gc_evaluator_callback); |
gc_evaluator_callback=0; |
} |
|
tmp2=num_objects; |
|
#ifdef DEBUG |
if(t_flag) |
fprintf(stderr,"Garbage collecting ... "); |
if(num_objects < 0) |
fatal("Panic, less than zero objects!\n"); |
#endif |
|
objects_alloced*=MULTIPLIER; |
objects_alloced += (double) num_allocs; |
|
objects_freed*=MULTIPLIER; |
objects_freed += (double) num_objects; |
|
|
|
|
|
tmp3=my_log2(num_objects); |
|
if(!d_flag) tmp3-=2; |
if(tmp3<0) tmp3=0; |
if(tmp3>=(long)NELEM(hashprimes)) tmp3=NELEM(hashprimes)-1; |
hashsize=hashprimes[tmp3]; |
|
hash=(struct marker **)xalloc(sizeof(struct marker **)*hashsize); |
MEMSET((char *)hash,0,sizeof(struct marker **)*hashsize); |
markers_left_in_chunk=0; |
|
|
gc_check_all_arrays(); |
gc_check_all_multisets(); |
gc_check_all_mappings(); |
gc_check_all_programs(); |
gc_check_all_objects(); |
call_callback(& gc_callbacks, (void *)0); |
|
|
gc_mark_all_arrays(); |
gc_mark_all_multisets(); |
gc_mark_all_mappings(); |
gc_mark_all_programs(); |
gc_mark_all_objects(); |
|
if(d_flag) |
gc_mark_all_strings(); |
|
|
gc_free_all_unreferenced_arrays(); |
gc_free_all_unreferenced_multisets(); |
gc_free_all_unreferenced_mappings(); |
gc_free_all_unreferenced_programs(); |
gc_free_all_unreferenced_objects(); |
|
|
|
free((char *)hash); |
while((m=chunk)) |
{ |
chunk=m->next; |
free((char *)m); |
} |
|
destruct_objects_to_destruct(); |
|
objects_freed -= (double) num_objects; |
|
tmp=(double)num_objects; |
tmp=tmp * GC_CONST/100.0 * (objects_alloced+1.0) / (objects_freed+1.0); |
|
if((int)tmp < alloc_threshold + num_allocs) |
{ |
alloc_threshold=(int)tmp; |
}else{ |
alloc_threshold+=num_allocs; |
} |
|
if(alloc_threshold < MIN_ALLOC_THRESHOLD) |
alloc_threshold = MIN_ALLOC_THRESHOLD; |
if(alloc_threshold > MAX_ALLOC_THRESHOLD) |
alloc_threshold = MAX_ALLOC_THRESHOLD; |
num_allocs=0; |
|
#ifdef DEBUG |
if(t_flag) |
fprintf(stderr,"done (freed %ld of %ld objects).\n", |
(long)(tmp2-num_objects),(long)tmp2); |
#endif |
|
#ifdef ALWAYS_GC |
ADD_GC_CALLBACK(); |
#else |
if(d_flag > 3) ADD_GC_CALLBACK(); |
#endif |
in_gc=0; |
} |
|
#endif |
|
|
|