home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
lifeos2.zip
/
LIFE-1.02
/
SOURCE
/
MEMORY.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-16
|
41KB
|
1,691 lines
/* Copyright 1991 Digital Equipment Corporation.
** All Rights Reserved.
*****************************************************************/
/* $Id: memory.c,v 1.10 1995/07/27 19:03:24 duchier Exp $ */
#ifndef lint
static char vcid[] = "$Id: memory.c,v 1.10 1995/07/27 19:03:24 duchier Exp $";
#endif /* lint */
/* need stdlib.h to declare atof */
#include <stdlib.h>
#include "extern.h"
#include "print.h"
#include "login.h"
#include "lefun.h"
#include "token.h"
#include "error.h"
#include "xpred.h"
#include "modules.h" /* RM: Jan 13 1993 */
/* #include <malloc.h> 11.9 */
/* external variables */
GENERIC mem_base;
GENERIC mem_limit;
GENERIC stack_pointer;
GENERIC heap_pointer;
GENERIC other_base;
GENERIC other_limit;
GENERIC other_pointer;
static long delta;
#ifdef prlDEBUG
static long amount_used;
#endif
#ifdef CLIFE
long pass;
#else
static long pass;
#endif /* CLIFE */
#define LONELY 1
#ifndef OS2_PORT
static struct tms last_garbage_time;
#else
static float last_garbage_time;
#endif
static float gc_time, life_time;
int mem_size;
int alloc_words;
#define ALIGNUP(X) { (X) = (GENERIC)( ((long) (X) + (ALIGN-1)) & ~(ALIGN-1) ); }
/************* STUFF FOR PARSING COMMAND LINE ARGS ************************/
char *GetStrOption(name,def)
char *name;
char *def;
{
int i;
char *result=def;
int l=strlen(name);
for(i=1;i<arg_c;i++)
if(arg_v[i][0]=='-' && (int)strlen(arg_v[i])>=l+1)
if(!strncmp(arg_v[i]+1,name,l))
if(arg_v[i][l+1]=='=')
result=arg_v[i]+l+2;
else
result=arg_v[i]+l+1;
return result;
}
int GetBoolOption(name)
char *name;
{
char *s;
s=GetStrOption(name,"off");
return strcmp(s,"off");
}
int GetIntOption(name,def)
char *name;
int def;
{
char *s;
char buffer[40];
sprintf(buffer,"%d",def);
s=GetStrOption(name,buffer);
return atof(s);
}
/****************************************************************************/
void pchoices() /* RM: Oct 28 1993 For debugging. */
{
ptr_choice_point c;
printf("stack pointer is: %x\n",stack_pointer);
for(c=choice_stack;c;c=c->next)
printf("\tc=%x\ts=%x\tg=%x\tu=%x\n",c,c->stack_top,c->goal_stack,c->undo_point);
}
/****************************************************************************/
/* GC sanity checks */
/* Keep for release versions, unless the garbage collector is very robust */
/* #define GCTEST */
/* Remove for release versions */
/* #define GCVERBOSE */
#ifndef GCTEST
#undef assert
#define assert(N)
#endif
void print_undo_stack()
{
ptr_stack u=undo_stack;
while (u) {
if (u->a<mem_base || u->a>mem_limit ||
(GENERIC)u->next<mem_base || (GENERIC)u->next>mem_limit) {
printf("UNDO: type:%ld a:%lx b:%lx next:%lx\n",u->type,u->a,u->b,u->next);
fflush(stdout);
}
u=u->next;
}
}
long bounds_undo_stack()
/* Address field in undo_stack is within range */
/* The only valid address outside this range is that of xevent_state */
{
ptr_stack u=undo_stack;
while (u) {
if ( (GENERIC)u<mem_base
|| (GENERIC)u>mem_limit
|| (!VALID_ADDRESS(u->a) && !(u->type & undo_action))
) {
if ((GENERIC)u<mem_base || (GENERIC)u>mem_limit) {
printf("\nUNDO: u=%lx\n",(long)u);
}
else {
printf("\nUNDO: u:%lx type:%ld a:%lx b:%lx next:%lx\n",
(long)u,u->type,u->a,u->b,u->next);
}
fflush(stdout);
return FALSE;
}
u=u->next;
}
return TRUE;
}
/****************************************************************************/
/* Forward declarations */
static void check_psi_list();
static void check_resid_list(); /* 21.9 */
static void check_choice();
static void check_undo_stack();
/******** FAIL_ALL()
This routines provokes a total failure, in case of a bad error
(out of memory, abort, etc...).
All goals are abandoned.
*/
void fail_all()
{
output_stream=stdout;
choice_stack=NULL;
goal_stack=NULL;
undo_stack=NULL;
abort_life(TRUE);
/* printf("\n*** Abort\n"); */
stdin_cleareof();
open_input_file("stdin");
}
/******************************************************************************
GARBAGE COLLECTING.
*/
void check_attr();
void check_psi_term();
void check_definition();
void check_resid_block();
/* RM: Jan 29 1993 Replaced with PVR's version of 26.1 */
/******** COMPRESS()
This routine compresses the memory contents and
calculates the new addresses. First the Stack is compressed, bottom up.
Secondly the Heap is compressed, top down.
*/
static void compress()
{
GENERIC addr, new_addr;
long len, i;
/* Compressing the stack: */
addr=new_addr=mem_base;
while (addr<=stack_pointer) {
len = *(addr+delta);
if (len) {
/* There are lots of these: */
/* if (len==LONELY) printf("Isolated LONELY at %lx\n",addr); */
if (len==LONELY) len=ALIGN;
else if (len & (ALIGN-1)) len=len-(len & (ALIGN-1))+ALIGN;
/* if (len & (ALIGN-1)) len=len-(len & (ALIGN-1))+ALIGN; 12.6 */
assert((len & (ALIGN-1))==0);
len /= sizeof(*addr);
assert(len>0);
for (i=0; i<len; i++) {
*new_addr = *addr;
if (i>0) {
if (*(addr+delta)>=len)
assert(i>0 ? *(addr+delta)<len : TRUE);
}
assert(VALID_ADDRESS(new_addr));
*(addr+delta) = (long)new_addr + 1; /* Set low bit */
#ifdef prlDEBUG
if (*(addr+delta) & 1 == 0)
printf ("compress: could be a bug ...\n");
#endif
addr++;
new_addr++;
}
}
else
addr++;
}
other_pointer=stack_pointer; /* 10.6 this var. is unused */
stack_pointer=new_addr;
/* Compressing the heap: */
addr=new_addr=mem_limit;
addr--; /* PVR fix: adding this statement avoids accessing beyond */
/* the memory's edge, which causes a segmentation fault on*/
/* SPARC. */
while (addr>=heap_pointer) {
skip_addr:
len= *(addr+delta);
if (len) {
if (len!=LONELY) {
if (len & (ALIGN-1)) len=len-(len & (ALIGN-1))+ALIGN;
assert((len & (ALIGN-1))==0);
len /= sizeof (*addr);
assert(len>0);
} else { /* len==LONELY */
GENERIC a;
if (len & (ALIGN-1)) len=len-(len & (ALIGN-1))+ALIGN;
assert((len & (ALIGN-1))==0);
len /= sizeof (*addr);
assert(len==1);
/* Check if the LONELY field is actually part of a block. */
/* If so, skip to the beginning of the block. */
a=addr;
do {
a--;
} while (a>=heap_pointer &&
(*(a+delta)==0 || *(a+delta)==LONELY));
if (a>=heap_pointer && *(a+delta)/sizeof(*a)+a>addr) {
addr=a;
goto skip_addr;
}
}
/* Move a block or an isolated LONELY field. */
addr += len;
for (i=0; i<len; i++) {
addr--;
new_addr--;
*new_addr = *addr;
assert(VALID_ADDRESS(new_addr));
*(addr+delta) = (long)new_addr + 1;
}
}
addr--;
}
heap_pointer=new_addr;
}
#define UNCHECKED(P) (! *((GENERIC)(P)+delta))
/******** UNCHECKED(p,l)
P is a pointer to a structure L bytes in length.
If L=LONELY then that means that P is a pointer to a sub-field of a
structure.
The function returns TRUE if the structure has not been yet thoroughly
explored, otherwise FALSE.
If this is the second pass then it translates P to its new value
(as calculated by COMPRESS).
*/
#ifdef CLIFE
long unchecked (p, len)
#else
static long unchecked (p, len)
#endif /* CLIFE */
GENERIC *p;
long len;
{
GENERIC addr;
long result=FALSE, value;
assert(len>0);
if ((unsigned long)*p>MAX_BUILT_INS) {
#ifdef GCTEST
if (!VALID_ADDRESS(*p)) {
printf("p=%lx,*p=%lx\n",p,*p);
}
#endif
assert(VALID_ADDRESS(*p));
addr = *p + delta;
value = *addr;
switch (pass) {
case 1:
#ifdef GCTEST
if (FALSE /* len>100 || value>100 13.8 */) {
/* This does in fact happen */
printf("len=%ld,value=%ld\n",len,value);
fflush(stdout);
}
#endif
/* if (!value) */
if (!value || value==LONELY) {
/* Pointer not yet explored */
result=TRUE;
*addr=len;
#ifdef prlDEBUG
amount_used+=len;
#endif
}
else if (value < len && len != LONELY) {
Errorline("in garbage collection, %d < %d.\n", value, len);
}
else if (value > len && len != LONELY) {
Errorline("in garbage collection, %d > %d.\n", value, len);
}
break;
case 2:
if (value & 1) { /* If low bit set */
value--; /* Reset low bit */
*addr=value;
#ifdef prlDEBUG
amount_used+=len;
#endif
result=TRUE;
}
if (!VALID_ADDRESS(value))
assert(VALID_ADDRESS(value));
*p = (GENERIC) value;
break;
}
}
return result;
}
/******** CHECK_STRING(s)
Claim the memory used by the string S.
*/
static void check_string (s)
GENERIC *s;
{
GENERIC addr;
long value;
long bytes;
if ((unsigned long) *s > MAX_BUILT_INS) {
switch (pass) {
case 1:
bytes=strlen((char *)*s)+1;
/* if (bytes==LONELY) {
fprintf(stderr,"Caught an empty string!\n");
fflush(stderr);
} */
/* Make sure there's no conflict with LONELY (this occurs for an */
/* empty string, which still needs a single byte of storage). */
/* This does occasionally happen. */
unchecked(s, (bytes==LONELY)?bytes+1:bytes);
break;
case 2:
addr=(*s+delta);
value= *addr;
if (value & 1) { /* If low bit set */
value--;
*s=(GENERIC)value;
*addr=value;
#ifdef prlDEBUG
amount_used+=strlen(*s)+1;
#endif
}
*s=(GENERIC)value;
break;
}
}
}
/* DENYS: BYTEDATA */
/******** CHECK_BYTEDATA(s)
Claim the memory used by a block of bytes
*/
static void check_bytedata(s)
GENERIC *s;
{
GENERIC addr;
long value;
if ((unsigned long) *s > MAX_BUILT_INS) {
unsigned long bytes = *((unsigned long *) *s);
unsigned long size = bytes + sizeof(bytes);
switch (pass) {
case 1:
unchecked(s,size);
break;
case 2:
addr=(*s+delta);
value= *addr;
if (value & 1) {
value--;
*s=(GENERIC) value;
*addr=value;
#ifdef prlDEBUG
amount_used+=size;
#endif
}
*s=(GENERIC)value;
break;
}
}
}
/******** CHECK_CODE(c)
Claim the memory used by a type code (=list of integers).
*/
static void check_code(c)
ptr_int_list *c;
{
while (unchecked(c,sizeof(int_list)))
c= &((*c)->next);
}
/******** CHECK_PAIR_LIST
Checks a list of <GOAL,BODY> pairs.
*/
static void check_pair_list(p)
ptr_pair_list *p;
{
while (unchecked(p,sizeof(pair_list))) {
check_psi_term(&((*p)->a));
check_psi_term(&((*p)->b));
p= &((*p)->next);
}
}
/******** CHECK_TRIPLE_LIST
Checks a list of <GOAL,BODY,DEF> triples.
*/
static void check_triple_list(p)
ptr_triple_list *p;
{
while (unchecked(p,sizeof(triple_list))) {
check_psi_term(&((*p)->a));
check_psi_term(&((*p)->b));
check_definition(&((*p)->c));
p= &((*p)->next);
}
}
/******** CHECK_KIDS(c)
Check a list of parents or children of a given type.
*/
static void check_kids(c)
ptr_int_list *c;
{
while (unchecked(c,sizeof(int_list))) {
check_definition(&((*c)->value));
c= &((*c)->next);
}
}
/******** CHECK_OPERATOR_DATA(op)
Explore a list of operator declarations.
*/
static void check_operator_data(op)
ptr_operator_data *op;
{
while (unchecked(op,sizeof(operator_data))) {
op = &((*op)->next);
}
}
static void check_module();
void check_hash_table(); /* RM: Feb 3 1993 */
static void check_keyword(); /* RM: Jan 12 1993 */
/******** CHECK_MODULE_LIST(c)
Check a list of modules.
*/
static void check_module_list(c) /* RM: Jan 12 1993 */
ptr_int_list *c;
{
while (unchecked(c,sizeof(int_list))) {
check_module(&((*c)->value));
c= &((*c)->next);
}
}
/******** CHECK_MODULE_TREE
This goes through the module table, checking all nodes.
*/
static void check_module_tree(n) /* RM: Jan 13 1993 */
ptr_node *n;
{
if (unchecked(n,sizeof(node))) {
check_module_tree(&((*n)->left));
check_string(&((*n)->key));
check_module(&((*n)->data));
check_module_tree(&((*n)->right));
}
}
/******** CHECK_MODULE(m)
Checks a module.
*/
static void check_module(m) /* RM: Jan 12 1993 */
ptr_module *m;
{
if(unchecked(m,sizeof(struct wl_module))) {
check_string(&((*m)->module_name));
check_string(&((*m)->source_file));
check_module_list(&((*m)->open_modules));
check_module_list(&((*m)->inherited_modules));
check_hash_table((*m)->symbol_table);
}
}
/******** CHECK_HASH_TABLE(table)
Check a hash table of keywords. The actual table is not stored within LIFE
memory.
*/
void check_hash_table(table) /* RM: Feb 3 1993 */
ptr_hash_table table;
{
long i;
for(i=0;i<table->size;i++)
if(table->data[i])
check_keyword(&(table->data[i]));
}
/******** CHECK_KEYWORD(k)
Checks a keyword.
*/
static void check_keyword(k) /* RM: Jan 12 1993 */
ptr_keyword *k;
{
if(unchecked(k,sizeof(struct wl_keyword))) {
check_module(&((*k)->module));
check_string(&((*k)->symbol));
check_string(&((*k)->combined_name));
check_definition(&((*k)->definition));
}
}
/******** CHECK_DEFINITION
This goes through the type tree which contains the parents and children lists
for all types, and the attributed code. The code field is not checked as
this has been done separately by CHECK_GAMMA.
*/
void check_definition(d)
ptr_definition *d;
{
if(unchecked(d,sizeof(definition))) {
check_keyword(&((*d)->keyword)); /* RM: Jan 12 1993 */
#ifdef prlDEBUG
printf("%lx %20s %ld\n",*d,(*d)->keyword->symbol,amount_used);
#endif
check_code(&((*d)->code));
check_pair_list(&((*d)->rule));
check_triple_list(&((*d)->properties));
if ((*d)->type==type) {
check_kids(&((*d)->parents));
check_kids(&((*d)->children));
}
check_psi_term(&((*d)->global_value)); /* RM: Feb 9 1993 */
check_psi_term(&((*d)->init_value)); /* RM: Mar 23 1993 */
check_operator_data(&((*d)->op_data)); /* PVR 5.6 */
#ifdef CLIFE
check_block_def(&((*d)->block_def)); /* RM: Jan 27 1993 */
#endif /* CLIFE */
}
}
/******** CHECK_DEFINITION_LIST
This checks the entire list of definitions.
*/
void check_definition_list() /* RM: Feb 15 1993 */
{
ptr_definition *d;
d= &first_definition;
while(*d) {
check_definition(d);
d= &((*d)->next);
}
}
/******** CHECK_DEF_CODE(d)
This routine checks the CODE field in a definition.
It may only be invoked by CHECK_GAMMA.
*/
static void check_def_code(d)
ptr_definition *d;
{
if (unchecked(d,sizeof(definition)))
check_code(&((*d)->code));
/* p = &((*d)->properties); */
/* check_def_prop(p); */
}
/******** CHECK_DEF_REST(d)
This routine checks the other fields in a definition.
It may only be invoked by CHECK_GAMMA_REST.
*/
static void check_def_rest(d)
ptr_definition *d;
{
if (*d) {
check_keyword(&((*d)->keyword)); /* RM: Jan 12 1993 */
check_pair_list(&((*d)->rule));
check_triple_list(&((*d)->properties));
if ((*d)->type==type) {
check_kids(&((*d)->parents));
check_kids(&((*d)->children));
}
check_operator_data(&((*d)->op_data)); /* PVR 5.6 */
#ifdef CLIFE
check_block_def(&((*d)->block_def)); /*CB 25/01/93 */
#endif /* CLIFE */
}
}
/******** CHECK_SYMBOL
This goes through the symbol table, checking all nodes, symbols, strings
and definitions not contained in the type table.
*/
static void check_symbol(n)
ptr_node *n;
{
if (unchecked(n,sizeof(node))) {
check_symbol(&((*n)->left));
check_string(&((*n)->key));
check_keyword(&((*n)->data)); /* RM: Jan 12 1993 */
check_symbol(&((*n)->right));
}
}
/******** CHECK_TYPE_DISJ
Checks the list of definitions appearing in a type disjunction.
*/
static void check_type_disj(p)
ptr_int_list *p;
{
while (unchecked(p,sizeof(int_list))) {
check_definition(&((*p)->value));
p= &((*p)->next);
}
}
/******** CHECK_GOAL_STACK
Check the goal_stack. This is quite complicated as each type of goal (prove,
unify, eval, eval_cut etc...) gives its own meanings to the three other
fields (A,B and C) present in each goal.
*/
static void check_goal_stack(g)
ptr_goal *g;
{
while (unchecked(g,sizeof(goal))) {
switch ((*g)->type) {
case fail:
break;
case unify:
case unify_noeval: /* PVR 5.6 */
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
break;
case prove:
check_psi_term(&((*g)->a));
if ((unsigned long)(*g)->b!=DEFRULES) check_pair_list(&((*g)->b));
check_pair_list(&((*g)->c));
break;
case disj:
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
break;
case what_next:
/* check_choice(&((*g)->b)); */
break;
case eval:
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
check_pair_list(&((*g)->c));
break;
case load:
check_psi_term(&((*g)->a));
check_string(&((*g)->c));
break;
case match:
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
check_resid_block(&((*g)->c));
break;
case general_cut:
/* assert((GENERIC)(*g)->a <= (GENERIC)choice_stack); 12.7 17.7 */
if (pass==1 && (ptr_choice_point)(*g)->a>choice_stack)
(*g)->a=(ptr_psi_term)choice_stack;
unchecked(&((*g)->a),LONELY);
break;
case eval_cut:
check_psi_term(&((*g)->a));
/* assert((GENERIC)(*g)->b <= (GENERIC)choice_stack); 12.7 17.7 */
if (pass==1 && (ptr_choice_point)(*g)->b>choice_stack)
(*g)->b=(ptr_psi_term)choice_stack;
unchecked(&((*g)->b),LONELY);
check_resid_block(&((*g)->c));
break;
case freeze_cut:
case implies_cut:
check_psi_term(&((*g)->a));
/* assert((GENERIC)(*g)->b <= (GENERIC)choice_stack); 12.7 17.7 */
if (pass==1 && (ptr_choice_point)(*g)->b>choice_stack)
(*g)->b=(ptr_psi_term)choice_stack;
unchecked(&((*g)->b),LONELY);
check_resid_block(&((*g)->c));
break;
case type_disj:
check_psi_term(&((*g)->a));
check_type_disj(&((*g)->b));
break;
case clause:
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
unchecked(&((*g)->c),LONELY);
/* check_pair_list((*g)->c); */ /* 6.8 */
break;
case del_clause:
check_psi_term(&((*g)->a));
check_psi_term(&((*g)->b));
unchecked(&((*g)->c),LONELY);
/* check_pair_list((*g)->c); */ /* 6.8 */
break;
case retract:
unchecked(&((*g)->a),LONELY);
/* check_pair_list((*g)->a); */ /* 6.8 */
/*PVR*/ /* check_choice(&((*g)->b)); 9.6 */
break;
default:
Errorline("in garbage collection, bad goal on stack.\n");
}
g= &((*g)->next);
}
}
/******** CHECK_RESID(r)
Explore a list of residuations.
*/
static void check_resid(r)
ptr_residuation *r;
{
ptr_int_list code;
ptr_list *l;
while (unchecked(r,sizeof(residuation))) {
if ((*r)->sortflag) /* 22.9 */
check_definition(&((*r)->bestsort));
else
check_code(&((*r)->bestsort)); /* 21.9 */
/* Handling of the value field (6.10) */
code = (*r)->sortflag ? ((ptr_definition)((*r)->bestsort))->code
: (ptr_int_list)(*r)->bestsort;
/* Copied (almost) verbatim from check_psi_term: */
if ((*r)->value) {
if (code==alist->code) { /* RM: Dec 15 1992 Will be removed */
l=(ptr_list *) &((*r)->value);
if (l)
printf("Found an old list!!\n");
}
else if (sub_CodeType(code,real->code))
unchecked(&((*r)->value),sizeof(REAL));
else if (sub_CodeType(code,quoted_string->code))
check_string(&((*r)->value));
/* DENYS: BYTEDATA */
else if (sub_CodeType(code,sys_bytedata->code))
check_bytedata(&((*r)->value));
else if (sub_CodeType(code,cut->code)) {
if (pass==1 && (*r)->value>(GENERIC)choice_stack)
(*r)->value=(GENERIC)choice_stack;
unchecked(&((*r)->value),LONELY);
}
else if (sub_CodeType(code,variable->code)) /* 8.8 */
check_string(&((*r)->value));
}
check_goal_stack(&((*r)->goal));
r= &((*r)->next);
}
}
/******** CHECK_RESID_BLOCK(rb)
Explore a residuation block.
*/
void check_resid_block(rb)
ptr_resid_block *rb;
{
if (*rb) {
if (unchecked(rb,sizeof(resid_block))) {
check_goal_stack(&((*rb)->ra));
check_resid_list(&((*rb)->rv)); /* 21.9 */
/* unchecked(&((*rb)->rl),LONELY); 12.6 */ /* 10.6 */
unchecked(&((*rb)->md),LONELY); /* 10.6 */
/* check_goal_stack(&((*rb)->rl)); 10.6 */
/* check_psi_term(&((*rb)->md)); 10.6 */
}
}
}
/******** CHECK_PSI_TERM(t)
Explore a psi_term.
*/
void check_psi_term(t)
ptr_psi_term *t;
{
ptr_list *l;
while (unchecked(t,sizeof(psi_term))) {
/* A psi-term on the heap has no residuation list. */
if (pass==1 && (GENERIC)(*t)>=heap_pointer && (GENERIC)(*t)<mem_limit) {
assert((*t)->resid==NULL);
}
check_definition(&((*t)->type));
check_attr(&((*t)->attr_list));
if ((*t)->value) {
if ((*t)->type==alist) { /* RM: Dec 15 1992 Should be removed */
l=(ptr_list *) &((*t)->value);
if (l)
printf("Found an old list!\n");
}
else
if (sub_type((*t)->type,real))
unchecked(&((*t)->value),sizeof(REAL));
else if (sub_type((*t)->type,quoted_string))
check_string(&((*t)->value));
/* DENYS: BYTEDATA */
else if (sub_type((*t)->type,sys_bytedata))
check_bytedata(&((*t)->value));
#ifdef CLIFE
else if ((*t)->type->type==block) { /* RM: Jan 27 1993 */
check_block_value(&((*t)->value));
}
#endif /* CLIFE */
else if ((*t)->type==cut) { /* RM: Oct 28 1993 */
/* assert((*t)->value <= (GENERIC)choice_stack); 12.7 17.7 */
if (pass==1 && (*t)->value>(GENERIC)choice_stack)
(*t)->value=(GENERIC)choice_stack;
unchecked(&((*t)->value),LONELY);
}
else if (sub_type((*t)->type,variable)) /* 8.8 */
check_string(&((*t)->value));
else if ((*t)->type!=stream)
Errorline("non-NULL value field in garbage collector, type='%s', value=%d.\n",
(*t)->type->keyword->combined_name,
(*t)->value);
}
/* check_psi_term(&((*t)->coref)); 9.6 */
if ((*t)->resid)
check_resid(&((*t)->resid));
t = &((*t)->coref);
}
}
/******** CHECK_ATTR(attribute-tree)
Check an attribute tree.
(Could improve this by randomly picking left or right subtree
for last call optimization. This would never overflow, even on
very skewed attribute trees.)
*/
void check_attr(n)
ptr_node *n;
{
while (unchecked(n,sizeof(node))) {
check_attr(&((*n)->left));
check_string(&((*n)->key));
check_psi_term(&((*n)->data));
n = &((*n)->right);
/* check_attr(&((*n)->right)); 9.6 */
}
}
/******** CHECK_GAMMA_CODE()
Check and update the code
reversing table. In this part, only the codes are checked in
the definitions, this is vital because these codes are used
later to distinguish between the various data types and to
determine the type of the VALUE field in psi_terms. Misunderstanding this
caused a lot of bugs in the GC.
*/
void check_gamma_code()
{
long i;
if (unchecked(&gamma_table,type_count*sizeof(ptr_definition))) {
for (i=0;i<type_count;i++)
check_def_code(&(gamma_table[i]));
}
}
/******** CHECK_GAMMA_REST()
Check and update the code reversing table.
*/
static void check_gamma_rest()
{
long i;
for (i=0;i<type_count;i++)
check_def_rest(&(gamma_table[i]));
}
/******** CHECK_UNDO_STACK()
This looks after checking the addresses of objects pointed to in the trail.
The type of the pointer to be restored on backtracking is known, which
allows the structure it is referring to to be accordingly checked.
*/
static void check_undo_stack(s)
ptr_stack *s;
{
while (unchecked(s,sizeof(stack))) {
switch((*s)->type) {
case psi_term_ptr:
check_psi_term(&((*s)->b));
break;
case resid_ptr:
check_resid(&((*s)->b));
break;
case int_ptr:
/* int_ptr's are used to trail time_stamps, so they can get large. */
break;
case def_ptr:
check_definition(&((*s)->b));
break;
case code_ptr:
check_code(&((*s)->b));
break;
case goal_ptr:
check_goal_stack(&((*s)->b));
break;
case cut_ptr: /* 22.9 */
break;
#ifdef CLIFE
case block_ptr: /* CB: Jan 28 1993 */
check_block_value(&((*s)->b));
break;
#endif /* CLIFE */
/* All undo actions here */
case destroy_window:
case show_window:
case hide_window:
/* No pointers to follow */
break;
}
s= &((*s)->next);
}
}
/******** CHECK_CHOICE(c)
This routine checks all choice points.
*/
static void check_choice_structs(c)
ptr_choice_point *c;
{
while(unchecked(c,sizeof(choice_point))) {
c= &((*c)->next);
}
}
static void check_choice(c)
ptr_choice_point *c;
{
while(*c) {
check_undo_stack(&((*c)->undo_point)); /* 17.7 */
check_goal_stack(&((*c)->goal_stack));
c= &((*c)->next);
}
}
/******** CHECK_SPECIAL_ADDRESSES
Here we check all the addresses which do not point to a whole data structure,
but to something within, for example a field such as VALUE which might
have been modified in a PSI_TERM structure. These are the LONELY addresses.
*/
static void check_special_addresses()
{
ptr_choice_point c;
ptr_stack p;
ptr_goal g;
c=choice_stack;
while(c) {
/* unchecked(&(c->undo_point),LONELY); 17.7 */
unchecked(&(c->stack_top),LONELY);
c=c->next;
}
p=undo_stack;
while (p) {
if (!(p->type & undo_action)) {
/* Only update an address if it's within the Life data space! */
if (VALID_RANGE(p->a)) unchecked(&(p->a),LONELY);
if (p->type==cut_ptr) unchecked(&(p->b),LONELY); /* 22.9 */
}
p=p->next;
}
}
/******** CHECK_PSI_LIST
Update all the values in the list of residuation variables, which is a list
of psi_terms.
*/
static void check_psi_list(l)
ptr_int_list *l;
{
while(unchecked(l,sizeof(int_list))) {
check_psi_term(&((*l)->value));
l= &((*l)->next);
}
}
/******** CHECK_RESID_LIST
Update all the values in the list of residuation variables, which is a list
of pairs of psi_terms.
*/
static void check_resid_list(l)
ptr_resid_list *l;
{
while(unchecked(l,sizeof(resid_list))) {
check_psi_term(&((*l)->var));
check_psi_term(&((*l)->othervar));
l= &((*l)->next);
}
}
/******** CHECK_VAR(t)
Go through the VARiable tree.
(This could be made tail recursive.)
*/
static void check_var(n)
ptr_node *n;
{
if (unchecked(n,sizeof(node))) {
check_var(&((*n)->left));
check_string(&((*n)->key));
check_psi_term(&((*n)->data));
check_var(&((*n)->right));
}
}
/******** CHECK
This routine checks all pointers and psi_terms to find out which memory cells
must be preserved and which can be discarded.
This routine explores all known structures. It is vital that it should visit
them all exactly once. It thus creates a map of what is used in memory, which
COMPRESS uses to compact the memory and recalculate the addresses.
Exploration of these structures should be done in exactly the same order
in both passes. If it is the second pass, pointers are assigned their new
values.
A crucial property of this routine: In pass 2, a global variable (i.e. a
root for GC) must be updated before it is accessed. E.g. don't use the
variable goal_stack before updating it.
*/
static void check()
{
#ifdef prlDEBUG
amount_used=0;
#endif
/* First of all, get all the codes right so that data type-checking remains
coherent.
Kids and Parents cannot be checked because the built-in types have codes
which might have been moved.
*/
/* print_undo_stack(); */
check_choice_structs(&choice_stack); /* RM: Oct 28 1993 */
assert((pass==1?bounds_undo_stack():TRUE));
check_gamma_code();
/* Now, check the rest of the definitions and all global roots */
check_gamma_rest();
assert((pass==1?bounds_undo_stack():TRUE));
check_definition(&abortsym); /* 26.1 */
check_definition(&aborthooksym); /* 26.1 */
check_definition(&add_module1); /* RM: Mar 12 1993 */
check_definition(&add_module2);
check_definition(&add_module3);
check_definition(&and);
check_definition(&apply);
check_definition(&boolean);
check_definition(&boolpredsym);
check_definition(&built_in);
check_definition(&colonsym);
check_definition(&commasym);
check_definition(&comment);
/* check_definition(&conjunction); 19.8 */
check_definition(&constant);
check_definition(&cut);
check_definition(&disjunction);
check_definition(&disj_nil); /* RM: Feb 16 1993 */
check_definition(&eof);
check_definition(&eqsym);
check_definition(&false);
check_definition(&funcsym);
check_definition(&functor);
check_definition(&iff);
check_definition(&integer);
check_definition(&alist);
check_definition(&life_or); /* RM: Apr 6 1993 */
check_definition(&minus_symbol); /* RM: Jun 21 1993 */
check_definition(&nil); /* RM: Dec 9 1992 */
check_definition(¬hing);
check_definition(&predsym);
check_definition("e);
check_definition("ed_string);
check_definition(&real);
check_definition(&stream);
check_definition(&succeed);
check_definition(&such_that);
check_definition(&top);
check_definition(&true);
check_definition(×ym);
check_definition(&tracesym); /* 26.1 */
check_definition(&typesym);
check_definition(&variable);
check_definition(&opsym);
check_definition(&loadsym);
check_definition(&dynamicsym);
check_definition(&staticsym);
check_definition(&encodesym);
check_definition(&listingsym);
/* check_definition(&provesym); */
check_definition(&delay_checksym);
check_definition(&eval_argsym);
check_definition(&inputfilesym);
check_definition(&call_handlersym);
check_definition(&xf_sym);
check_definition(&fx_sym);
check_definition(&yf_sym);
check_definition(&fy_sym);
check_definition(&xfx_sym);
check_definition(&xfy_sym);
check_definition(&yfx_sym);
check_definition(&nullsym);
/* RM: Jul 7 1993 */
check_definition(&final_dot);
check_definition(&final_question);
check_sys_definitions();
#ifdef X11
check_definition(&xevent);
check_definition(&xmisc_event);
check_definition(&xkeyboard_event);
check_definition(&xbutton_event);
check_definition(&xconfigure_event);
check_definition(&xmotion_event);
check_definition(&xenter_event);
check_definition(&xleave_event);
check_definition(&xexpose_event);
check_definition(&xdestroy_event);
check_definition(&xdisplay);
check_definition(&xdrawable);
check_definition(&xwindow);
check_definition(&xpixmap);
check_definition(&xgc);
check_definition(&xdisplaylist);
#endif
/* check_psi_term(&empty_list); 5.8 */
check_string(&one);
check_string(&two);
check_string(&three);
check_string(&year_attr);
check_string(&month_attr);
check_string(&day_attr);
check_string(&hour_attr);
check_string(&minute_attr);
check_string(&second_attr);
check_string(&weekday_attr);
check_psi_term(&input_state);
check_psi_term(&stdin_state);
check_psi_term(&error_psi_term);
check_psi_term(&saved_psi_term);
check_psi_term(&old_saved_psi_term);
check_psi_term(&null_psi_term);
check_psi_term(&old_state); /* RM: Feb 17 1993 */
assert((pass==1?bounds_undo_stack():TRUE));
#ifdef X11
check_psi_term(&xevent_list);
check_psi_term(&xevent_existing);
#endif
check_choice(&choice_stack);
/* check_choice(&prompt_choice_stack); 12.7 */
/* RM: Feb 3 1993 */
/* check_symbol(&symbol_table); */
/* check_definition(&first_definition); */
check_definition_list(); /* RM: Feb 15 1993 */
/*** MODULES ***/
/* RM: Jan 13 1993 */
check_module_tree(&module_table);
check_module(&sys_module);
check_module(&bi_module);
check_module(&user_module); /* RM: Jan 27 1993 */
check_module(&no_module);
check_module(&x_module);
check_module(&syntax_module);
check_module(¤t_module);
/*** End ***/
check_var(&var_tree);
check_goal_stack(&goal_stack);
check_goal_stack(&aim); /* 7.10 */
if (TRUE /*resid_aim 10.6 */) check_resid_list(&resid_vars); /* 21.9 */
check_goal_stack(&resid_aim);
assert((pass==1?bounds_undo_stack():TRUE));
check_undo_stack(&undo_stack);
assert((pass==1?bounds_undo_stack():TRUE));
check_special_addresses();
assert((pass==1?bounds_undo_stack():TRUE));
}
void print_gc_info(timeflag)
long timeflag;
{
fprintf(stderr," [%ld%% free (%ldK), %ld%% heap, %ld%% stack",
(100*((unsigned long)heap_pointer-(unsigned long)stack_pointer)+mem_size/2)/mem_size,
((unsigned long)heap_pointer-(unsigned long)stack_pointer+512)/1024,
(100*((unsigned long)mem_limit-(unsigned long)heap_pointer)+mem_size/2)/mem_size,
(100*((unsigned long)stack_pointer-(unsigned long)mem_base)+mem_size/2)/mem_size);
if (timeflag) {
fprintf(stderr,", %1.3fs cpu (%ld%%)",
gc_time,
(unsigned long)(0.5+100*gc_time/(life_time+gc_time)));
}
fprintf(stderr,"]\n");
}
/******** GARBAGE()
The garbage collector.
This routine is called whenever memory is getting low.
It returns TRUE if insufficient memory was freed to allow
the interpreter to carry on working.
This is a half-space GC, it first explores all known structures, then
compresses the heap and the stack, then during the second pass assigns
all the new addresses.
Bugs will appear if the collector is called during parsing or other
such routines which are 'unsafe'. In order to avoid this problem, before
one of these routines is invoked the program will check to see whether
there is enough memory available to work, and will call the GC if not
(this is a fix, because it is not possible to determine in advance
what the size of a psi_term read by the parser will be).
*/
void garbage()
{
GENERIC addr;
#ifndef OS2_PORT
struct tms garbage_start_time,garbage_end_time;
#else
float garbage_start_time,garbage_end_time;
#endif
long start_number_cells, end_number_cells;
start_number_cells = (stack_pointer-mem_base) + (mem_limit-heap_pointer);
times(&garbage_start_time);
/* Time elapsed since last garbage collection */
#ifndef OS2_PORT
life_time=(garbage_start_time.tms_utime - last_garbage_time.tms_utime)/60.0;
#else
life_time=(garbage_start_time - last_garbage_time)/60.0;
#endif
if (verbose) {
fprintf(stderr,"*** Garbage Collect "); /* RM: Jan 26 1993 */
fprintf(stderr,"\n*** Begin");
print_gc_info(FALSE);
fflush(stderr);
}
/* reset the other base */
for (addr = other_base; addr < other_limit; addr ++)
*addr = 0;
pass=1;
check();
#ifdef GCVERBOSE
fprintf(stderr,"- Done pass 1 ");
#endif
assert(bounds_undo_stack());
compress();
#ifdef GCVERBOSE
fprintf(stderr,"- Done compress ");
#endif
pass=2;
check();
assert(bounds_undo_stack());
#ifdef GCVERBOSE
fprintf(stderr,"- Done pass 2\n");
#endif
clear_copy();
printed_pointers=NULL;
pointer_names=NULL;
times(&garbage_end_time);
#ifndef OS2_PORT
gc_time=(garbage_end_time.tms_utime - garbage_start_time.tms_utime)/60.0;
#else
gc_time=(garbage_end_time - garbage_start_time)/60.0;
#endif
garbage_time+=gc_time;
if (verbose) {
fprintf(stderr,"*** End ");
print_gc_info(TRUE); /* RM: Jan 26 1993 */
stack_info(stderr);
fflush(stderr);
}
last_garbage_time=garbage_end_time;
end_number_cells = (stack_pointer-mem_base) + (mem_limit-heap_pointer);
assert(end_number_cells<=start_number_cells);
ignore_eff=FALSE;
}
/****************************************************************************
MEMORY ALLOCATION ROUTINES.
*/
/******** HEAP_ALLOC(s)
This returns a pointer to S bytes of memory in the heap.
Alignment is taken into account in the following manner:
the macro ALIGN is supposed to be a power of 2 and the pointer returned
is a multiple of ALIGN.
*/
GENERIC heap_alloc (s)
long s;
{
if (s & (ALIGN-1))
s = s - (s & (ALIGN-1))+ALIGN;
/* assert(s % sizeof(*heap_pointer) == 0); */
s /= sizeof (*heap_pointer);
heap_pointer -= s;
if (stack_pointer>heap_pointer)
Errorline("the heap overflowed into the stack.\n");
return heap_pointer;
}
/******** STACK_ALLOC(s)
This returns a pointer to S bytes of memory in the stack.
Alignment is taken into account in the following manner:
the macro ALIGN is supposed to be a power of 2 and the pointer returned
is a multiple of ALIGN.
*/
GENERIC stack_alloc(s)
long s;
{
GENERIC r;
r = stack_pointer;
if (s & (ALIGN-1))
s = s - (s & (ALIGN-1)) + ALIGN;
/* assert(s % sizeof(*stack_pointer) == 0); */
s /= sizeof (*stack_pointer);
stack_pointer += s;
if (stack_pointer>heap_pointer)
Errorline("the stack overflowed into the heap.\n");
return r;
}
/******** INIT_MEMORY()
Get two big blocks of memory to work in.
The second is only used for the half-space garbage collector.
The start and end addresses of the blocks are re-aligned correctly.
to allocate.
*/
void init_memory ()
{
alloc_words=GetIntOption("memory",ALLOC_WORDS);
mem_size=alloc_words*sizeof(long);
mem_base = (GENERIC) malloc(mem_size);
other_base = (GENERIC) malloc(mem_size);
if (mem_base && other_base) {
/* Rewrote some rather poor code... RM: Mar 1 1994 */
ALIGNUP(mem_base);
stack_pointer = mem_base;
mem_limit=mem_base+alloc_words-2;
ALIGNUP(mem_limit);
heap_pointer = mem_limit;
ALIGNUP(other_base);
other_pointer = other_base;
other_limit=other_base+alloc_words-2;
ALIGNUP(other_limit);
delta = other_base - mem_base;
buffer = (char *) malloc (PRINT_BUFFER); /* The printing buffer */
/* RM: Oct 22 1993 */
/* Fill the memory with rubbish data */
/*
{
int i;
for(i=0;i<alloc_words;i++) {
mem_base[i]= -1234;
other_base[i]= -1234;
}
}
*/
}
else
Errorline("Wild_life could not allocate sufficient memory to run.\n\n");
}
/******** MEMORY_CHECK()
This function tests to see whether enough memory is available to allow
execution to continue. It causes a garbage collection if not, and if that
fails to release enough memory it returns FALSE.
*/
long memory_check ()
{
long success=TRUE;
if (heap_pointer-stack_pointer < GC_THRESHOLD) {
if(verbose) fprintf(stderr,"\n"); /* RM: Feb 1 1993 */
garbage();
/* Abort if didn't recover at least GC_THRESHOLD/10 of memory */
if (heap_pointer-stack_pointer < GC_THRESHOLD+GC_THRESHOLD/10) {
fprintf(stderr,"*********************\n");
fprintf(stderr,"*** OUT OF MEMORY ***\n");
fprintf(stderr,"*********************\n");
fail_all();
success=FALSE;
}
}
return success;
}