stk/Src/gc.c

636 lines
17 KiB
C

/*
*
* g c . c -- Mark and Sweep Garbage Collector
*
*
* Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
*
*
* Permission to use, copy, modify, distribute,and license this
* software and its documentation for any purpose is hereby granted,
* provided that existing copyright notices are retained in all
* copies and that this notice is included verbatim in any
* distributions. No written agreement, license, or royalty fee is
* required for any of the authorized uses.
* This software is provided ``AS IS'' without express or implied
* warranty.
*
* Author: Erick Gallesio [eg@unice.fr]
* Creation date: 17-Feb-1993 12:27
* Last file update: 3-Sep-1999 20:20 (eg)
*
*/
#include "stk.h"
#include "gc.h"
#include "extend.h"
#ifdef USE_STKLOS
# include "stklos.h"
#endif
#ifdef USE_TK
# include "tk-glue.h"
#endif
#define MIN_HEAP 100 /* A too small value cause an infinite loop */
#define VALID_ADDRESS(heap_org, p) \
((p >= heap_org) && (p < heap_org + heap_size) && \
(((((char *)p) - ((char *)heap_org)) % sizeof(struct obj)) == 0))
#define HEAPS_INCREMENT 10
#define gc_mark(ptr) STk_gc_mark(ptr)
struct gc_protected {
SCM *location;
struct gc_protected *next;
};
/* exported vars */
SCM STk_freelist;
SCM *STk_stack_start_ptr;
double STk_total_gc_time = 0.0;
long STk_alloc_cells;
int STk_gc_requested = 0;
/* internal vars */
static jmp_buf save_regs_gc_mark;
static long gc_cells_collected;
static long heap_size = INITIAL_HEAP_SIZE;
static int gc_verbose = 0;
static int gc_calls = 0;
static SCM *heaps = NULL;
static int heaps_length = 0;
static int heaps_used = 0;
static double time_gc_start;
static struct gc_protected *protected_registers = NULL;
static void no_memory(void)
{
panic("No more memory. Cannot allocate a new heap.");
}
static void allocate_new_heap(void)
{
SCM ptr, next;
SCM heap_org, heap_end;
/* Don't use must_malloc here since it can conduct to call GC when
* allocating large heaps (typically with large -cells xxx)
*/
if (heaps_used == heaps_length) {
/* Realloc heaps */
heaps_length += HEAPS_INCREMENT;
heaps = (heaps_used) ? realloc(heaps, heaps_length*sizeof(SCM)):
malloc(heaps_length*sizeof(SCM));
if (!heaps) no_memory();
}
ptr = (SCM) malloc(sizeof(struct obj)*heap_size);
if (ptr) {
heap_org = heaps[heaps_used++] = ptr;
heap_end = heap_org + heap_size;
}
else
no_memory();
/* Prepare heap space */
for(ptr = heap_org, next=ptr+1; ptr < heap_end; ptr=next, next=ptr+1) {
ptr->type = tc_free_cell;
ptr->cell_info = 0;
ptr->gc_mark = 0;
CDR(ptr) = (next < heap_end) ? next : STk_freelist;
}
STk_freelist = heap_org;
if (gc_verbose)
Fprintf(STk_curr_eport,
";; [new heap allocated (%d/%d)]\n",
heaps_used,heaps_length);
}
void STk_gc_count_cells(long *allocated, long *used, long* calls)
{
register SCM ptr, heap_org, heap_end;
register long used_cells = 0L;
int i;
for (i=0; i < heaps_used; i++) {
heap_org = heaps[i];
heap_end = heap_org + heap_size;
for(ptr = heap_org; ptr < heap_end; ptr++)
if (NTYPEP(ptr, tc_free_cell)) used_cells += 1;
}
*allocated = heaps_used * heap_size;
*used = used_cells;
*calls = (long) gc_calls;
}
int STk_valid_address(SCM p) /* True if p is a valid address. Used for #Pxyz */
{
int i;
for(i=0; i < heaps_used; i++) {
register SCM heap_org=heaps[i];
if (VALID_ADDRESS(heap_org, p)) return TRUE;
}
return FALSE;
}
void STk_gc_mark(SCM ptr)
{
Top:
if (NULLP(ptr) || SMALL_CSTP(ptr)) return;
if (ptr->gc_mark) return;
ptr->gc_mark = GC_MARK;
switch (TYPE(ptr)) {
case tc_nil: return;
case tc_cons: gc_mark(CAR(ptr));ptr = CDR(ptr); goto Top;
case tc_flonum: return;
case tc_integer: return;
case tc_bignum: return;
case tc_symbol: ptr = VCELL(ptr);goto Top;
case tc_keyword: return;
case tc_subr_0: return;
case tc_subr_1: return;
case tc_subr_2: return;
case tc_subr_3: return;
case tc_subr_0_or_1: return;
case tc_subr_1_or_2: return;
case tc_subr_2_or_3: return;
case tc_lsubr: return;
case tc_ssubr: return;
case tc_fsubr: return;
case tc_syntax: return;
case tc_closure: gc_mark(CLOSCODE(ptr));
ptr = CLOSENV(ptr);
goto Top;
case tc_free_cell: /* -----> Error */
case tc_char: return;
case tc_string: return;
case tc_vector: {
long j;
for(j = 0;j < ptr->storage_as.vector.dim; j++)
gc_mark(ptr->storage_as.vector.data[j]);
return;
}
case tc_eof: return;
case tc_undefined: return;
case tc_iport:
case tc_oport: gc_mark(PORT_REVENT(ptr));
ptr = PORT_WEVENT(ptr);
goto Top;
case tc_isport: return;
case tc_osport: return;
case tc_ivport:
case tc_ovport: STk_mark_virtual_port(ptr);
return;
case tc_boolean: return;
case tc_macro: ptr = ptr->storage_as.macro.code; goto Top;
case tc_localvar: ptr = ptr->storage_as.localvar.symbol; goto Top;
case tc_globalvar: ptr = VCELL(ptr); goto Top;
case tc_modulevar: ptr = CAR(ptr); goto Top;
case tc_cont: STk_mark_continuation(ptr);
return;
case tc_env:
case tc_address: ptr = ptr->storage_as.env.data;
goto Top;
case tc_autoload: gc_mark(CAR(ptr)); ptr = CDR(ptr); goto Top;
case tc_Cpointer: return;
case tc_module: STk_mark_module(ptr); return;
case tc_frame: gc_mark(CAR(ptr));ptr = CDR(ptr); goto Top;
case tc_values: ptr = CAR(ptr); goto Top;
#ifdef USE_STKLOS
case tc_instance: {
/* ACCESSORS_OF(ptr) doesn't need to be marked since it
* is always accessible from SLOTS_OF(CLASS_OF(ptr))
*/
long j;
gc_mark(CLASS_OF(ptr));
for (j = 0; j < NUMBER_OF_SLOTS(ptr); j++)
gc_mark(THE_SLOT_OF(ptr, j));
return;
}
case tc_next_method: gc_mark(CAR(ptr)); ptr = CDR(ptr); goto Top;
#endif
#ifdef USE_TK
case tc_tkcommand: ptr = ptr->storage_as.tk.l_data; goto Top;
case tc_tclobject: ptr = CAR(ptr); if (ptr) goto Top;
return;
#endif
case tc_quote: return;
case tc_lambda: return;
case tc_if: return;
case tc_setq: return;
case tc_cond: return;
case tc_and: return;
case tc_or: return;
case tc_let: return;
case tc_letstar: return;
case tc_letrec: return;
case tc_begin: return;
case tc_promise: ptr = ptr->storage_as.promise.expr; goto Top;
case tc_apply:
case tc_call_cc: return;
case tc_dynwind: return;
case tc_extend_env: return;
case tc_unbound: return;
default: if (EXTENDEDP(ptr)) {STk_extended_mark(ptr); return;}
}
/* if we are here, it's an implementation error. Signal it */
Fprintf(STk_curr_eport, "INTERNAL ERROR: trying to mark %lx (type=%d)\n",
(unsigned long) ptr, TYPE(ptr));
}
static void gc_sweep(void)
{
SCM ptr, heap_org, heap_end, nfreelist;
long n;
int i;
n = 0;
nfreelist = NIL;
for (i = 0; i < heaps_used; i++) {
/* Sweep a heap */
heap_org = heaps[i];
heap_end = heap_org + heap_size;
for (ptr=heap_org; ptr < heap_end; ptr++) {
if (ptr->gc_mark == 0) {
switch (TYPE(ptr)) {
case tc_nil: break;
case tc_cons: break;
case tc_flonum: break;
case tc_integer: break;
case tc_bignum: mpz_clear(BIGNUM(ptr)); free(BIGNUM(ptr)); break;
case tc_symbol: STk_free_symbol(ptr); break;
case tc_keyword: STk_free_keyword(ptr); break;
case tc_subr_0: break;
case tc_subr_1: break;
case tc_subr_2: break;
case tc_subr_3: break;
case tc_subr_0_or_1: break;
case tc_subr_1_or_2: break;
case tc_subr_2_or_3: break;
case tc_lsubr: break;
case tc_ssubr: break;
case tc_fsubr: break;
case tc_syntax: break;
case tc_closure: break;
case tc_free_cell: break;
case tc_char: break;
case tc_string: free(ptr->storage_as.string.data); break;
case tc_vector: free(ptr->storage_as.vector.data); break;
case tc_eof: break;
case tc_undefined: break;
case tc_iport:
case tc_oport: STk_freeport(ptr); break;
case tc_isport:
case tc_osport: STk_free_string_port(ptr); break;
case tc_ivport:
case tc_ovport: STk_free_virtual_port(ptr); break;
case tc_boolean: break;
case tc_macro: break;
case tc_localvar: break;
case tc_globalvar: break;
case tc_modulevar: break;
case tc_cont: free(ptr->storage_as.cont.data); break;
case tc_env: break;
case tc_address: break;
case tc_autoload: break;
case tc_Cpointer: if (!EXTSTATICP(ptr)) free(EXTDATA(ptr)); break;
case tc_module: STk_free_module(ptr); break;
case tc_frame: break;
case tc_values: break;
#ifdef USE_STKLOS
case tc_instance: STk_free_instance(ptr); break;
case tc_next_method: break;
#endif
#ifdef USE_TK
case tc_tkcommand: if (! ptr->storage_as.tk.data->deleted)
STk_internal_Tcl_DeleteCommand
(STk_main_interp, ptr->storage_as.tk.data->Id);
free(ptr->storage_as.tk.data);
break;
case tc_tclobject: break;
#endif
case tc_quote: break;
case tc_lambda: break;
case tc_if: break;
case tc_setq: break;
case tc_cond: break;
case tc_and: break;
case tc_or: break;
case tc_let: break;
case tc_letstar: break;
case tc_letrec: break;
case tc_begin: break;
case tc_promise: break;
case tc_apply: break;
case tc_call_cc: break;
case tc_dynwind: break;
case tc_extend_env: break;
case tc_unbound: break;
default: if (EXTENDEDP(ptr))
STk_extended_sweep(ptr);
else
Fprintf(STk_curr_eport,
"FATAL ERROR: trying to sweep %lx (type=%d)\n",
(unsigned long) ptr, TYPE(ptr));
}
/* Declare this cell free and put it in free list */
ptr->type = tc_free_cell;
ptr->cell_info = 0;
CDR(ptr) = nfreelist;
nfreelist = ptr;
n += 1;
}
else
ptr->gc_mark = 0;
}
}
gc_cells_collected = n;
STk_freelist = nfreelist;
}
void STk_mark_stack(SCM *start, SCM *end)
{
register SCM p, heap_org;
register long i, j, n;
if (start > end) {
SCM *tmp;
tmp = start; start = end; end = tmp;
}
n = end - start;
if (gc_verbose)
Fprintf(STk_curr_eport, "[Marking zone <0x%lx->0x%lx> (%ld words)]\n",
(unsigned long) start, (unsigned long) end, (unsigned long) n);
for(j=0; j<n; j++) {
p = start[j];
/* if p looks as a SCM pointer mark location */
for (i=0; i < heaps_used; i++) {
heap_org = heaps[i];
if (VALID_ADDRESS(heap_org, p) && NTYPEP(p,tc_free_cell)) gc_mark(p);
}
}
}
static void mark_protected(void)
{
struct gc_protected *reg;
/* Mark protected vars */
for(reg = protected_registers; reg; reg = reg->next) gc_mark(*(reg->location));
/* Mark all objects accessible from obarray */
STk_mark_symbol_table();
/* Mark the module table */
STk_mark_module_table();
/* Mark the signal table */
STk_mark_signal_table();
/* Mark the table of traced variables */
STk_mark_tracevar_table();
#ifdef USE_TK
/* Mark all Tcl/Tk callbacks */
STk_mark_callbacks();
#endif
}
static void gc_start(void)
{
time_gc_start = STk_my_time();
gc_calls += 1;
gc_cells_collected = 0;
gc_verbose = STk_lookup_variable(GC_VERBOSE, NIL) != Ntruth;
if (gc_verbose) Puts(";; [starting GC]\n", STk_curr_eport);
}
static void gc_end(void)
{
long total_cells, used_cells;
double time_for_this_gc;
total_cells = heaps_used * heap_size;
used_cells = total_cells - gc_cells_collected;
time_for_this_gc = STk_my_time() - time_gc_start;
STk_total_gc_time += time_for_this_gc;
/*
* If heap is more than 75% filled after gc, allocate a new heap to
* avoid continuous GCs
*/
if (((float) used_cells / total_cells) > 0.75) allocate_new_heap();
STk_gc_requested = 0;
if (gc_verbose)
Fprintf(STk_curr_eport, ";; [end of GC (cells used: %ld/%ld; time: %.2fms)]\n",
used_cells, total_cells, time_for_this_gc);
}
static void gc_mark_and_sweep(void)
{
long prev_context = STk_err_handler->context;
SCM stack_end; /* The topmost variable (at least a SCM) allocated on stack */
/**** Disallow interrupts while GC'ing because signal handlers may cons */
STk_ignore_signals();
STk_err_handler->context = ERR_FATAL;
gc_start();
/**** Marking phase */
setjmp(save_regs_gc_mark); /* registers */
STk_mark_stack((SCM *) save_regs_gc_mark,
(SCM *) (((char *) save_regs_gc_mark)+
sizeof(save_regs_gc_mark)));
STk_mark_stack((SCM *) STk_stack_start_ptr, (SCM *) &stack_end); /* stack */
mark_protected(); /* globals */
/**** Sweep phase */
gc_sweep();
/* Re-allow signals */
gc_end();
STk_err_handler->context = prev_context;
STk_allow_signals();
/* send the pseudo-signal SIGHADGC to say that we had a finished GC'ing */
STk_signal_GC();
}
void STk_gc_for_newcell(void)
{
if (STk_err_handler->context != ERR_FATAL) {
gc_mark_and_sweep();
if (NNULLP(STk_freelist)) return;
}
panic("Out of storage");
}
PRIMITIVE STk_gc(void)
{
gc_mark_and_sweep();
return UNDEFINED;
}
void STk_gc_protect(SCM *location)
{
struct gc_protected *reg;
reg = (struct gc_protected *) must_malloc(sizeof(struct gc_protected));
reg->location = location;
reg->next = protected_registers;
protected_registers = reg;
}
void STk_gc_unprotect(SCM *location)
{
struct gc_protected *reg, *prev;
for (prev=NULL, reg=protected_registers; reg; prev=reg, reg=reg->next)
if (reg->location == location) {
if (prev)
prev->next = reg->next;
else
protected_registers = reg->next;
free(reg);
return;
}
}
PRIMITIVE STk_gc_stats(void)
{
int i, freq[tc_stop_extd+1];
register SCM ptr, heap_org, heap_end;
long used_cells = 0L;
/* Reset array of frequencies */
for (i=0; i <=tc_stop_extd; i++) freq[i] = 0;
/* Fill the frequencies array */
for (i=0; i < heaps_used; i++) {
heap_org = heaps[i];
heap_end = heap_org + heap_size;
for(ptr = heap_org; ptr < heap_end; ptr++) {
if (NTYPEP(ptr, tc_free_cell)) {
used_cells += 1;
freq[TYPE(ptr)] += 1;
}
}
}
/* Print statistics */
Puts(";; GC statistics\n", STk_curr_eport);
Puts(";; -------------\n", STk_curr_eport);
Fprintf(STk_curr_eport, ";; cells used %ld/%ld\n", used_cells,
heaps_used*heap_size);
Fprintf(STk_curr_eport, ";; # of used heaps %d\n", heaps_used);
Fprintf(STk_curr_eport, ";; # of GC calls %d (time spent in GC %.2fms)\n",
gc_calls, STk_total_gc_time);
for (i=0; i <= tc_stop_extd; i++)
if (freq[i]) Fprintf(STk_curr_eport, "(%d %d) ", i, freq[i]);
Puts("\n;;\n", STk_curr_eport);
Flush(STk_curr_eport);
return UNDEFINED;
}
#ifdef DEBUG_STK
PRIMITIVE STk_find_cells(SCM type)
{
SCM ptr, z, heap_org, heap_end;
int i,t,l;
if (NINTEGERP(type)) Err("%find-cells: bad integer", type);
t = INTEGER(type);
/* Count how many items we have */
for (i=l=0; i < heaps_used; i++) {
heap_org = heaps[i];
heap_end = heap_org + heap_size;
for (ptr=heap_org; ptr < heap_end; ptr++)
if (TYPEP(ptr, t)) l += 1;
}
/* Allocate a vector for the result */
z = STk_makevect(l, NULL);
l = 0;
/* Place all the items in the newly created vector */
for (i=l=0; i < heaps_used; i++) {
heap_org = heaps[i];
heap_end = heap_org + heap_size;
for (ptr=heap_org; ptr < heap_end; ptr++)
if (ptr != z && TYPEP(ptr, t))
VECT(z)[l++] = ptr;
}
return z;
}
#endif
PRIMITIVE STk_expand_heap(SCM arg)
{
int i, number_of_heaps, wanted;
if (NINTEGERP(arg)) Err("expand-heap: bad value", arg);
gc_verbose = STk_lookup_variable(GC_VERBOSE, NIL) != Ntruth;
wanted = INTEGER(arg);
number_of_heaps = (wanted + heap_size - 1) / heap_size;
for (i = heaps_used; i < number_of_heaps; i++)
allocate_new_heap();
return UNDEFINED;
}
/*=============================================================================*/
/* i n i t */
/*=============================================================================*/
#ifndef max
#define max(a,b) (((a)<(b))?(b):(a))
#endif
void STk_init_gc(void)
{
STk_freelist = NIL;
if (STk_arg_cells) {
/* Set the heap size to the specified value */
int tmp = atoi(STk_arg_cells);
if (tmp > 0) heap_size = max(tmp,MIN_HEAP);
}
allocate_new_heap();
}