/***************************************************************************
 *
 * Copyright (c) 1998-1999 Niels Möller
 * Copyright (c) 1999 BalaBit Computing
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: gc.c,v 1.13 2005/05/03 15:49:35 bazsi Exp $
 *
 ***************************************************************************/

#include "objects.h"

#include <assert.h>

/* Global variables */
static struct ol_object *all_objects = NULL;
static unsigned number_of_objects = 0;
static unsigned live_objects = 0;
unsigned gc_busy_threshold = 500; 
unsigned gc_idle_threshold = 50;

#if 0
static struct ol_object *globals = NULL;
#endif

#ifdef DEBUG_ALLOC
static void sanity_check_object_list(void)
{
  unsigned i = 0;
  struct ol_object *o;

#if 0
  wwrite("sanity_check_object_list: Objects on list:\n");
  for(o = all_objects; o; o = o->next)
    werror("  %xi, class: %z\n", (void *) o, o->isa ? o->isa->name : "UNKNOWN");
#endif
  
  for(o = all_objects; o; o = o->next)
    i++;

  if (i != number_of_objects)
    fatal("sanity_check_object_list: Found %i objects, expected %i.\n",
	  i, number_of_objects);
}
#endif

/* FIXME: This function recurses heavily. One could use some trickery
 * to emulate tail recursion, which would help marking linked list. Or
 * one could use some more efficient datastructures than the C stack
 * for keeping track of the marked but not yet traced objects. */
static void gc_mark(struct ol_object *o)
{
  static int depth = 0;
  if (!o)
    return;
  
  switch(o->alloc_method)
    {
    case OL_ALLOC_STACK:
      fatal("gc_mark: Unexpected stack object!\n");

    case OL_ALLOC_HEAP:
      if (o->marked)
	return;
      o->marked = 1;
      /* Fall through */
    case OL_ALLOC_STATIC:
      /* Can't use mark bit on static objects, as there's no way to
       * reset all the bits */
      assert(!o->dead);
      {
	struct ol_class *class;

#if 1
	debug("gc_mark: Marking object of class '%z' (%i)\n",
	      o->isa ? o->isa->name : "UNKNOWN", depth);
#endif

        depth++;
	for (class = o->isa; class; class = class->super_class)
	  {
	    if (class->mark_instance)
	      MARK_INSTANCE(class, o, gc_mark);
	  }
        depth--;
      }
      break;
    default:
      fatal("gc_mark: Memory corrupted!\n");
    }
}

static void gc_sweep(void)
{
  struct ol_object *o;
  struct ol_object **o_p;

  live_objects = 0;
  
  for(o_p = &all_objects; (o = *o_p); )
    {
      if (o->marked)
	{
	  /* Keep object */
	  live_objects++;
	  o->marked = 0;
	}
      else
	{
	  struct ol_class *class;

#if 0
	  debug("gc_sweep: Freeing object of class '%z'\n",
		o->isa->name);
#endif  
	  for (class = o->isa; class; class = class->super_class)
	    if (class->free_instance)
	      FREE_INSTANCE(class, o);

	  /* Unlink object */
	  *o_p = o->next;
	  number_of_objects--;
	  
	  ol_object_free(o);
	  continue;
	}
      o_p = &o->next;
    }
  assert(live_objects == number_of_objects);
}

void gc_register(struct ol_object *o)
{
#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif
  o->marked = o->dead = 0;
  o->next = all_objects;
  all_objects = o;

  number_of_objects ++;
#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif
}

#if 0
/* FIXME: This function is utterly broken, and should be deleted. The
 * problem is that the object must be unlinked from the all_objects
 * list before linked into the globals list. */
void gc_register_global(struct ol_object *o)
{
#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif
  o->marked = o->dead = 0;
  o->next = globals;
  globals = o;

#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif
}
#endif

/* FIXME: This function should really deallocate and forget the object
 * early. But we keep it until the next gc, in order to catch any
 * references to killed objects. */
void gc_kill(struct ol_object *o)
{
#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif

  if (!o)
    return;
  
  o->dead++;

#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif
}

void gc(struct ol_object *root)
{
  unsigned before = number_of_objects;

  gc_mark(root);  
  gc_sweep();
  
  verbose("Objects alive: %i, garbage collected: %i\n", live_objects,
	  before - live_objects);
}

extern UINT32 strcount;

void gc_maybe(struct ol_object *root, int busy)
{
#ifdef DEBUG_ALLOC
  sanity_check_object_list();
#endif

  if ((busy && (number_of_objects > gc_busy_threshold)) ||
      (!busy && (number_of_objects > gc_idle_threshold)))
    { 

      verbose("Garbage collecting while %z...\n", busy ? "busy" : "idle");
      gc(root);
    }


}


syntax highlighted by Code2HTML, v. 0.9.1