608 lines
16 KiB
C
608 lines
16 KiB
C
/*
|
|
*
|
|
* g c . c -- Mark and Sweep Garbage Collector
|
|
*
|
|
*
|
|
* Copyright © 1993-1996 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
|
|
*
|
|
*
|
|
* Permission to use, copy, and/or distribute this software and its
|
|
* documentation for any purpose and without fee is hereby granted, provided
|
|
* that both the above copyright notice and this permission notice appear in
|
|
* all copies and derived works. Fees for distribution or use of this
|
|
* software or derived works may only be charged with express written
|
|
* permission of the copyright holder.
|
|
* This software is provided ``as is'' without express or implied warranty.
|
|
*
|
|
* This software is a derivative work of other copyrighted softwares; the
|
|
* copyright notices of these softwares are placed in the file COPYRIGHTS
|
|
*
|
|
*
|
|
* Author: Erick Gallesio [eg@unice.fr]
|
|
* Creation date: 17-Feb-1993 12:27
|
|
* Last file update: 29-Aug-1996 11:46
|
|
*
|
|
*/
|
|
|
|
#include "stk.h"
|
|
#include "gc.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 no_memory(void)
|
|
{
|
|
STk_panic("**** No more memory. Cannot allocate a new heap. Stop\n");
|
|
}
|
|
|
|
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_stderr, ";; [new heap allocated (%d/%d)]\n",
|
|
heaps_used, heaps_length);
|
|
}
|
|
|
|
static void gc_start(void)
|
|
{
|
|
time_gc_start = STk_my_time();
|
|
gc_calls += 1;
|
|
gc_cells_collected = 0;
|
|
gc_verbose = (VCELL(Intern(GC_VERBOSE)) != Ntruth);
|
|
|
|
if (gc_verbose) fprintf(STk_stderr, ";; [starting GC]\n");
|
|
}
|
|
|
|
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_stderr, ";; [end of GC (cells used: %ld/%ld; time: %.2fms)]\n",
|
|
used_cells, total_cells, time_for_this_gc);
|
|
STk_handle_signal(SIGHADGC);
|
|
}
|
|
|
|
|
|
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(ptr->storage_as.closure.code);
|
|
ptr = ptr->storage_as.closure.env;
|
|
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_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_cont: ptr = STk_mark_continuation(ptr);
|
|
goto Top;
|
|
case tc_env:
|
|
case tc_address: ptr = ptr->storage_as.env.data;
|
|
goto Top;
|
|
case tc_autoload: ptr = CAR(ptr);
|
|
goto Top;
|
|
case tc_Cpointer: return;
|
|
#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;
|
|
#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_stderr, "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: free(ptr->storage_as.flonum.data);
|
|
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_boolean: break;
|
|
case tc_macro: break;
|
|
case tc_localvar: break;
|
|
case tc_globalvar: 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;
|
|
#ifdef USE_STKLOS
|
|
case tc_instance: free(INST(ptr)); break;
|
|
case tc_next_method: break;
|
|
#endif
|
|
#ifdef USE_TK
|
|
case tc_tkcommand: if (! ptr->storage_as.tk.data->deleted)
|
|
Tcl_internal_DeleteCommand
|
|
(STk_main_interp, ptr->storage_as.tk.data->Id);
|
|
free(ptr->storage_as.tk.data);
|
|
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_stderr,
|
|
"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_stderr, "[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 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_mark_and_sweep(void)
|
|
{
|
|
SCM stack_end; /* The topmost variable allocated on stack */
|
|
|
|
gc_start();
|
|
setjmp(save_regs_gc_mark);
|
|
STk_mark_stack((SCM *) save_regs_gc_mark,
|
|
(SCM *) (((char *) save_regs_gc_mark)+sizeof(save_regs_gc_mark)));
|
|
mark_protected();
|
|
STk_mark_stack((SCM *) STk_stack_start_ptr, (SCM *) &stack_end);
|
|
|
|
gc_sweep();
|
|
gc_end();
|
|
}
|
|
|
|
void STk_gc_for_newcell(void)
|
|
{
|
|
if (Error_context != ERR_FATAL) {
|
|
STk_disallow_sigint();
|
|
Error_context = ERR_FATAL;
|
|
gc_mark_and_sweep();
|
|
Error_context = ERR_OK;
|
|
STk_allow_sigint();
|
|
if (NNULLP(STk_freelist)) return;
|
|
}
|
|
Err("Out of storage",NIL);
|
|
}
|
|
|
|
|
|
PRIMITIVE STk_gc(void)
|
|
{
|
|
STk_disallow_sigint();
|
|
Error_context = ERR_FATAL;
|
|
gc_mark_and_sweep();
|
|
Error_context = ERR_OK;
|
|
STk_allow_sigint();
|
|
|
|
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 */
|
|
fprintf(STk_stderr, ";; GC statistics\n");
|
|
fprintf(STk_stderr, ";; -------------\n");
|
|
fprintf(STk_stderr, ";; cells used %ld/%ld\n", used_cells, heaps_used*heap_size);
|
|
fprintf(STk_stderr, ";; # of used heaps %d\n", heaps_used);
|
|
fprintf(STk_stderr, ";; # 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_stderr, "(%d %d) ", i, freq[i]);
|
|
fprintf(STk_stderr, "\n;;\n");
|
|
|
|
return UNDEFINED;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
PRIMITIVE STk_expand_heap(SCM arg)
|
|
{
|
|
int i, number_of_heaps, wanted;
|
|
|
|
if (NINTEGERP(arg)) Err("expand-heap: bad integer", arg);
|
|
|
|
gc_verbose = VCELL(Intern(GC_VERBOSE))!=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;
|
|
}
|
|
|
|
#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();
|
|
}
|