#ifndef lint
static char rcsid[] = "$Header: misc.c,v 1.4 91/05/06 12:24:33 dcurtis Exp $";
#endif
/* $Log:	misc.c,v $
 * Revision 1.4  91/05/06  12:24:33  dcurtis
 * added stacktop fix to work on ultrix 4.2 systems
 * i.e. calculate stacktop dynamically and do not round up
 * 
 * Revision 1.3  91/04/02  19:27:36  mtv
 * revised hblk allocation and when-to-gc policy to accurately bound
 * the number of active hblks.  Also fixed a bug in gc_realloc, but this
 * procedure is still suspect.
 * 
 * Revision 1.2  91/03/27  17:51:42  mtv
 * Added clu_alloc_atomic.
 * 
 * Revision 1.1  91/02/04  16:21:36  mtv
 * Initial revision
 * 
 */

#define DEBUG       /* Some run-time consistency checks */
#undef DEBUG
#define VERBOSE
#undef VERBOSE

#include <stdio.h>
#include <signal.h>
#define PORTABLE_CLU
# ifdef PORTABLE_CLU
#include "pclu_err.h"
#include "pclu_sys.h"
#else
#include "gc.h"
# endif

int dont_gc = 0;
extern long mem_found;

/*
 * Paramters used to control growth of the heap and frequency of
 * garbage collection.
 *
 * A block of HBLKSIZE bytes is considered "active" is some (possibly
 * garbage) objects reside on it.
 *
 * blks_hard_limit is an upper bound on the number of active blocks.
 * When allocation would exceed blks_soft_limit, a gc is performed.
 *
 * Required: blks_soft_limit <= blks_hard_limit
 * Sensible: blks_active <= blks_soft_limit
 */

int blks_active = 0;
int blks_hard_limit = MAX_HEAP_DEFAULT;
int blks_min_gc = FIRST_GC;
int blks_soft_limit = FIRST_GC;

static int addHBLK = add_DEFAULT;
static int maskHBLK = mask_DEFAULT;

/* adjust add & mask */
/* 1/24/94 mtv: {_}gcOPcontrol renamed to _gc_control */
errcode _gc_control(add, mask, elist)
CLUREF add;
CLUREF mask;
errlist elist;
{
  if (add.num < 0) signal(ERR_illegal);
  if (mask.num > add.num) signal(ERR_illegal);
  if (mask.num < HBLKSIZE) signal(ERR_illegal);
  addHBLK = divHBLKSZ(add.num);
  maskHBLK = divHBLKSZ(mask.num);
  signal(ERR_ok);
}

/* recompute next_gc */
void recompute_ngc()
{
int arm1 = 0;
int arm2 = 0;
int arm3 = 0;

  if (addHBLK == 0 && maskHBLK == 0)
    blks_soft_limit = ((blks_active+1+add_DEFAULT) & ~mask_DEFAULT) -1;
  else
    blks_soft_limit = ((blks_active+1+addHBLK) & ~maskHBLK) -1;
  
  if (blks_active == 0 || (float) (blks_soft_limit - blks_active) / (float) blks_active < 1.0 ) {
    arm1 = 1;
    blks_soft_limit = 2.0 * blks_active; }

  if (blks_soft_limit - blks_active < 1024) {
    arm2 = 1;
    blks_soft_limit = blks_active + 1024;}

  if (blks_soft_limit > blks_hard_limit) {
    arm3 = 1;
    blks_soft_limit = blks_hard_limit;}
  
  if (blks_soft_limit < blks_min_gc)
    blks_soft_limit = blks_min_gc;

  /* printf("SOFT %d mult_arm %d low_pass %d high_pass %d\n", 
		blks_soft_limit, arm1, arm2, arm3); */
}
   
# ifdef MERGE_SIZES
#   if MAXOBJSZ == MAXAOBJSZ
#       define MAXSZ MAXOBJSZ
#   else
	--> causes problems here, since we cant map any size to a
	    size that doesnt have a free list.  Either initialization
	    needs to be cleverer, or we need separate maps for atomic
	    and composite objects.
#   endif
    long size_map[MAXSZ+1];

    /* Set things up so that size_map[i] >= i, but not too much bigger */
    /* and so that size_map contains relatively few distinct entries   */
    void init_size_map()
    {
	register int i;
	register int i_rounded_up = 0;

	for (i = 1; i < 9; i++) {
#           ifdef ALIGN_DOUBLE
	      size_map[i] = (i + 1) & (~1);
#           else
	      size_map[i] = i;
#           endif
	}
	for (i = 9; i <= MAXSZ; i++) {
	    if (i_rounded_up < i) {
#               ifdef ALIGN_DOUBLE
		  i_rounded_up = (i + (i >> 1) + 1) & (~1);
#               else                                       
		  i_rounded_up = i + (i >> 1);             
#               endif
		if (i_rounded_up > MAXSZ) {
		    i_rounded_up = MAXSZ;
		}
	    }
	    size_map[i] = i_rounded_up;
	}
    }
# endif


/* allocate lb bytes of atomic data */

#ifdef PORTABLE_CLU
struct obj * gc_malloc_atomic(lb)
int lb;
{
CLUREF ans;
errcode err;

        clu_alloc_atomic(lb, &ans);
        return((struct obj *)ans.ref);
        }
void clu_alloc_atomic(lb, ans)
int lb;
char **ans;
#else
struct obj * gc_malloc_atomic(lb)
int lb;
#endif
{
register struct obj *op;
register struct obj **opp;
register int lw = BYTES_TO_WORDS(lb + (sizeof (word)) -1);

#   ifdef VERBOSE
	printf("Here we are in gc_malloc_atomic(%d)\n",lw);
#   endif
    if( lw <= MAXAOBJSZ ) {
#       ifdef MERGE_SIZES
	  lw = size_map[lw];
#       endif
	opp = &(aobjfreelist[lw]);
        if( (op = *opp) == ((struct obj *)0) ) {
	    op = _allocaobj(lw);
        }
#       ifdef DEBUG
	    if ((op -> obj_link != ((struct obj *) 0)
		&& (((unsigned)(op -> obj_link)) > ((unsigned) HEAPLIM)
		   || ((unsigned)(op -> obj_link)) < ((unsigned) HEAPSTART)))) {
		fprintf(stderr, "Bad free list in gc_malloc_atomic\n");
		abort(op);
            }
#       endif
        *opp = op->obj_link;
        op->obj_link = (struct obj *)0;
    } else {
	register struct hblk * h;
#ifndef DELAY_GC
	if (!sufficient_hb(-lw) && !dont_gc) {
            gcollect();
	}
#endif
#       ifdef VERBOSE
	    printf("gc_malloc_atomic calling allochblk(%x)\n",lw);
#	endif
	h = allochblk(-lw, blks_soft_limit);
#ifdef DELAY_GC
        if (h == (struct hblk*)0) {
                gcollect();
                h = allochblk(-lw, blks_hard_limit);
                if (h == (struct hblk*)0) {
#                    ifdef PORTABLE_CLU
                        _chanOP_save_tty();
#                    endif
                        write(2,"Out of Memory!  Giving up ...\n", 30);
                        exit(-1);
                }
        }
#endif
	add_hblklist(h);
	op = (struct obj *) (h -> hb_body);
    }
#ifdef PORTABLE_CLU
    *ans = (char *)op;
    bzero((char *) op, WORDS_TO_BYTES(lw));
#else
    return(op);
#endif


}

/* allocate lb bytes of possibly composite data */

int gcflag = 0;         /* is gc in progress? */
int gccount = 0;        /* count of gc's */

#ifdef PORTABLE_CLU
struct obj * gc_malloc(lb)
int lb;
{
CLUREF ans;
errcode err;

        clu_alloc(lb, &ans);
        return((struct obj *)ans.ref);
        }
void clu_alloc(lb, ans)
int lb;
char **ans;
#else
struct obj * gc_malloc(lb)
int lb;
#endif
{
register struct obj *op;
register struct obj **opp;
register int lw = BYTES_TO_WORDS(lb + (sizeof (word)) -1);

    if( lw <= MAXOBJSZ ) {
#       ifdef MERGE_SIZES
	  lw = size_map[lw];
#       endif
	opp = &(objfreelist[lw]);
        if( (op = *opp) == ((struct obj *)0) ) {
	    op = _allocobj(lw);
        }
#       ifdef DEBUG
	    if ((op -> obj_link != ((struct obj *) 0)
		&& (((unsigned)(op -> obj_link)) > ((unsigned) HEAPLIM)
		   || ((unsigned)(op -> obj_link)) < ((unsigned) HEAPSTART)))) {
		fprintf(stderr, "Bad free list in gc_malloc\n");
		abort(op);
            }
#       endif
        *opp = op->obj_link;
        op->obj_link = (struct obj *)0;
    } else {
	register struct hblk * h;
#ifndef DELAY_GC
	if (!sufficient_hb(lw) && !dont_gc) {
            gcollect();
	}
#endif
#       ifdef VERBOSE
	    printf("gc_malloc calling allochblk(%x)\n",lw);
#	endif
	h = allochblk(lw, blks_soft_limit);
#ifdef    DELAY_GC
        if (h == (struct hblk*)0) {
		gcollect();
		h = allochblk(lw, blks_hard_limit);
		if (h == (struct hblk*)0) {
#                    ifdef PORTABLE_CLU
                        _chanOP_save_tty();
#                    endif
                        write(2,"Out of Memory!  Giving up ...\n", 30);
                        exit(-1);
                }
        }
#endif

        add_hblklist(h);
	op = (struct obj *) (h -> hb_body);
    }
#ifdef PORTABLE_CLU
    *ans = (char *)op;
#else
    return(op);
#endif
}

void gc_free();

/* Change the size of the block pointed to by p to contain at least   */
/* lb bytes.  The object may be (and quite likely will be) moved.     */
/* The new object is assumed to be atomic if the original object was. */
/* Shrinking of large blocks is not implemented well.                 */
struct obj * gc_realloc(p,lb)
struct obj * p;
int lb;
{
register struct obj *op;
register struct obj **opp;
register struct hblk * h;
register int sz;    /* Size of old object in bytes */
int is_atomic;
int shrink_size = lb;

    h = HBLKPTR(p);
    sz = h -> hb_sz;
    if (sz < 0) {
	sz = -sz;
	is_atomic = TRUE;
    } else {
	is_atomic = FALSE;
    }
    sz = WORDS_TO_BYTES(sz);

    if (is_atomic) {
      if (sz > WORDS_TO_BYTES(MAXAOBJSZ)) {
	/* Round it up to the next whole heap block */
	  sz = (sz+sizeof(struct hblkhdr)+HBLKSIZE-1)
		& (~HBLKMASK);
	  sz -= sizeof(struct hblkhdr);
	  shrink_size += sizeof(struct hblkhdr);
      }
      if (lb <= sz) {
	if (shrink_size >= (sz >> 1)) {
	    /* Already big enough, but not too much bigger than object. */
	    /* Ignore the request.                                      */
	    /* If sz is big enough, we should probably deallocate       */
	    /* part of the heap block here, but ...                     */
	    return(p);
	} else {
	    /* shrink */
	      struct obj * result = gc_malloc_atomic(lb);

	      bcopy(p, result, lb);
	      gc_free(p);
	      return(result);
	}
      } else {
	/* grow */
	  struct obj * result = gc_malloc_atomic(lb);

	  bcopy(p, result, sz);
	  gc_free(p);
	  return(result);
      }
    } else /* composite */ {
      if (sz > WORDS_TO_BYTES(MAXOBJSZ)) {
	/* Round it up to the next whole heap block */
	  sz = (sz+sizeof(struct hblkhdr)+HBLKSIZE-1)
		& (~HBLKMASK);
	  sz -= sizeof(struct hblkhdr);
	  shrink_size += sizeof(struct hblkhdr);
      }
      if (lb <= sz) {
	if (shrink_size >= (sz >> 1)) {
	    return(p);
	} else {
	    /* shrink */
	      struct obj * result = gc_malloc(lb);

	      bcopy(p, result, lb);
	      gc_free(p);
	      return(result);
	}
      } else {
	/* grow */
	  struct obj * result = gc_malloc(lb);

	  bcopy(p, result, sz);
	  gc_free(p);
	  return(result);
      }
    }
}

/* Explicitly deallocate an object p */
void gc_free(p)
struct obj *p;
{
    register struct hblk *h;
    register int sz;
    register word * i;
    register word * limit;

    h = HBLKPTR(p);
    sz = h -> hb_sz;
    if (sz < 0) {
        sz = -sz;
        if (sz > MAXAOBJSZ) {
	    h -> hb_uninit = 1;
	    del_hblklist(h);
	    freehblk(h);
	} else {
	    p -> obj_link = aobjfreelist[sz];
	    aobjfreelist[sz] = p;
	}
    } else {
	/* Clear the object, other than link field */
	    limit = &(p -> obj_component[sz]);
	    for (i = &(p -> obj_component[1]); i < limit; i++) {
		*i = 0;
	    }
	if (sz > MAXOBJSZ) {
	    p -> obj_link = 0;
	    h -> hb_uninit = 0;
	    del_hblklist(h);
	    freehblk(h);
	} else {
	    p -> obj_link = objfreelist[sz];
	    objfreelist[sz] = p;
	}
    }
    /* Add it to mem_found to prevent anomalous heap expansion */
    /* in the event of repeated explicit frees of objects of   */
    /* varying sizes.                                          */
        mem_found += sz;
}


/*
 * Disable non-urgent signals
 */
int holdsigs()
{
    unsigned mask = 0xffffffff;

    mask &= ~(1<<(SIGSEGV-1));
    mask &= ~(1<<(SIGILL-1));
    mask &= ~(1<<(SIGBUS-1));
    mask &= ~(1<<(SIGIOT-1));
    mask &= ~(1<<(SIGEMT-1));
    mask &= ~(1<<(SIGTRAP-1));
    mask &= ~(1<<(SIGQUIT-1));
    return(sigsetmask(mask));
}

void gc_init()
{
    word dummy;
#   define STACKTOP_ALIGNMENT_M1 0xffffff

    heaplim = (char *) (sbrk(0));
/* #   ifdef HBLK_MAP */
/*	heapstart = (char *) (HBLKPTR(((unsigned)sbrk(0))+HBLKSIZE-1 )); */
	heapstart = HEAPSTART;
/* #   endif */
#   ifdef STACKTOP
	stacktop = STACKTOP;
#   else
#   ifdef MIPS
	stacktop = (word *)((long)(&dummy));
#   else
	stacktop = (word *)((((long)(&dummy)) + STACKTOP_ALIGNMENT_M1)
			    & ~STACKTOP_ALIGNMENT_M1);
#   endif
#   endif
    hincr = HINCR;
    expand_hp(hincr);
    init_hblklist();
#   ifdef MERGE_SIZES
      init_size_map();
#   endif
}

int sigsetmask(int mask)
{
	return mask;
}
