2005/5/9

     
 

MarkSweepGc.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library 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 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_core/src/acdk/lang/sys/MarkSweepGc.cpp,v 1.11 2005/05/02 23:05:45 kommer Exp $

#include <acdk.h>
#include <acdk/util/logging/Log.h>
#include <acdk/lang/Integer.h>

#include "Allocator.h"
#include "AllocatorInternals.h"

namespace acdk {
namespace lang {
namespace sys {

//#define DEBUG_PA
#define DOUTST(msgstr) sys::coreout << msgstr

#ifdef DEBUG_PA
#define DOUT(msgstr) sys::coreout << msgstr
#else //DEBUG_PA
#define DOUT(msgstr) do { } while(false)
#endif //DEBUG_PA

inline bool hasColletableFields(acdk::lang::Object* o)
{
  return true; // because DmiObject always return 0
  /*
  if (o->_isObjectRefFlag(ObjectBase::ObjectHasLocalGc) == true)
    return true;
  int colfields = o->getClazzInfo()->getCollectableFieldsCount();
  return colfields > 0;
  */
}


void
markAsExternal(acdk::lang::Object* obj)
{
  //DOUT("MARK: " << (void*) obj << sys::eofl);
  obj->_setObjectRefFlag(true, ObjectBase::Marked);
  obj->_setObjectRefFlag(true, ObjectBase::Visited);
  DOUT("GC: Mark Object: " << (void*)obj << ", " << obj->getClazzInfo()->name << sys::eofl);

  if (hasColletableFields(obj) == false)
    return;

  FieldReferences fields;
  obj->getCollectableFields(fields);
  for (int i = 0; i < fields.size(); i++) 
  {
    acdk::lang::Object* f = fields[i]->impl();
    if (f != 0 && f->_isObjectRefFlag(ObjectBase::Marked) == false)
      markAsExternal(f);
  }
}


bool
hasExternalReference(acdk::lang::Object* findobj, acdk::lang::Object* obj, ReferedMap& refmap)
{
  if (obj->_isObjectRefFlag(ObjectBase::Visited) == true)
    return obj->_isObjectRefFlag(ObjectBase::Marked);
  //obj->_setObjectRefFlag(true, ObjectBase::Visited);
  if (obj->_isObjectRefFlag(ObjectBase::Marked) == true)
    return true;
  ReferedMap::iterator rmend = refmap.end();
  ReferedMap::iterator rmit = refmap.find(obj);
  if (rmit == rmend)
    return false;
  obj->_setObjectRefFlag(true, ObjectBase::Visited);
  while (rmit != rmend) 
  {
    if ((*rmit).first == 0)
      break;
    if ((*rmit).first == obj) {
      ReferedMapValue* ovecptr = (*rmit).second;
      if (ovecptr == 0) {
        ++rmit;
        continue;
      }
      ObjectPtrVector& ovec = ovecptr->refered;
      for (int i = 0; i < ovec.size(); i++) {
        acdk::lang::Object* mobj = ovec[i];
        if (mobj == obj) { // ??
          //probably nonsense: ++rmit;
          continue;
        }
        if (refmap.getRefered(mobj) == 0) {
          markAsExternal(mobj);
          //obj->_setObjectRefFlag(false, ObjectBase::Visited);
          return true;
        }
        if (hasExternalReference(findobj, mobj, refmap) == true) {
          markAsExternal(obj);
          //obj->_setObjectRefFlag(false, ObjectBase::Visited);
          return true;
        }
      }
    }
    ++rmit;
  }
  //obj->_setObjectRefFlag(false, ObjectBase::Visited);
  return false;
}




bool
doGc(Allocator* allocator, AllocatorObjectsIterator* iterator, bool recursiv)
{
  
  int before_gc_elements;
  int before_gc_memory;
  {
    const AllocatorInfo& ainf = allocator->getAllocatorInfo();
    before_gc_elements = ainf.memTypes[ObjectMem].count;
    before_gc_memory = ainf.memTypes[ObjectMem].size;
  }
  
//#if 0
  //int before_gc_elements = allocator->getElementCount(UnspecifiedMem);
  //int before_gc_memory = allocator->getAllocatedCount(UnspecifiedMem);
  acdk::lang::Object* o = 0;
  int objectcount = 0;
  while ((o = iterator->getNextObject()) != 0)
  {
    //o = iterator->getCurrentElement().mem.obj;
    o->_setObjectRefFlag(false, ObjectBase::Marked);
    o->_setObjectRefFlag(false, ObjectBase::Visited);
    if (o->_isObjectRefFlag(ObjectBase::IsStaticRef) == true || 
        o->_isObjectRefFlag(ObjectBase::IsMemLocked) == true || 
        o->_isObjectRefFlag(ObjectBase::IsStackRef) == true //|| 
        //o->_isObjectRefFlag(ObjectBase::ObjectHasLocalGc) == true
        )
    {
      markAsExternal(o);
    }
    /*
    if (_lastAddress < o)
      _lastAddress = o;
    if (_firstAddress > o)
      _firstAddress = o;
    */
    ++objectcount;
  }
  iterator->reset();

  ReferedMap refmap((1 + objectcount) * 3);
  //HolderMap holdermap(mapsize);
  FieldReferences fields;
  // building backward ref
  while ((o = iterator->getNextObject()) != 0)
  {
    o = iterator->getCurrentElement().mem.obj;
    if (hasColletableFields(o) == false) 
      continue;
    
    fields.clear();
    o->getCollectableFields(fields);
    for (int i = 0; i < fields.size(); i++) 
    {
      if (*fields[i] != Nil) 
      {
              //DOUT(fields[i]->impl() << " refered by " <<  o << sys::eofl);
        refmap.addRefered(fields[i]->impl(), o);
        refmap.addHolder(fields[i]);
          //holdermap.add(fields[i]);
      }
    }
  }
  DOUT("[GC] marking" << sys::eofl);
  {
    ReferedMap::iterator it = refmap.begin();
    ReferedMap::iterator end = refmap.end();
    for (; it != end; ++it) 
    {
      acdk::lang::Object* robj = (*it).first;
      if (robj->magic() != _MagicObjectSig) 
       continue;

      if (hasExternalReference(robj, robj, refmap) == true) 
      {
        //DOUT("MARK: " << (void*)robj);
        robj->_setObjectRefFlag(true, ObjectBase::Marked);
      }
      
    }
  }
//#endif
   DOUT("[GC] sweeping" << sys::eofl);
RestartSweep:
  bool wasmodified = true;
  while (wasmodified == true) 
  {
    wasmodified = false;
    ReferedMap::iterator it = refmap.begin();
    ReferedMap::iterator end = refmap.end();
    
    for (; it != end; ++it) 
    {
      acdk::lang::Object* obj = (*it).first;
      if (obj == 0)
        continue;
      if (obj->magic() != _MagicObjectSig) 
          continue;
      //sys::coreout << "o:" << (void*)obj << sys::eofl;
      try {
        obj->testIntegrity();
      } 
      catch (...) 
      {
        ACDK_NLOG("acdk.lang.gc", Error, "acdk::lang::Object is insane: " + Integer::toHexString((int)(void*)obj));
        return false;
      }
      if (obj->_isObjectRefFlag(ObjectBase::Marked) == false) 
      {
        ObjectRefPtrVector& refvec = (*it).second->hold;
        bool restart = false;
        for (int i = 0; i < refvec.size(); i++) 
        {
          acdk::lang::Object* ref = refvec[i];
          if (ref == 0 || ref->impl() == 0)
            continue;
          if (ref->impl()->magic() != _MagicObjectSig) 
            continue;
          if (ref->impl() != obj)
            DOUT(" OOPS " << (void*)ref->impl() << " != " << obj << sys::eofl);
          if (obj->refCount() == 1) 
            restart = true;  
          DOUT("GC: Free Ref: " << (void*)ref << "; Object: " << (void*)ref->impl() << ", " << ref->impl()->getClazzInfo()->name << sys::eofl);
          *(ref) = Nil;
          if (restart == true) 
          {
            //?? refmap.erase(it);
            DOUT("[GC] restart" << sys::eofl);
            goto RestartSweep;
          }
          wasmodified = true;
        }
      }
    }
  }
  /*
  int after_gc_elements = _allocator->getElementCount(UnspecifiedMem);
  int after_gc_memory = _allocator->getAllocatedCount(UnspecifiedMem);
  */
  const AllocatorInfo& ainf = allocator->getAllocatorInfo();
  int after_gc_elements = ainf.memTypes[ObjectMem].count;
  int after_gc_memory = ainf.memTypes[ObjectMem].size;

  if (after_gc_elements == before_gc_elements)
    ACDK_NLOG("acdk.lang.gc", SysDebug, "GC released no objects");
  else 
    ACDK_NLOGP("acdk.lang.gc", SysDebug, "GC Released objects", LOG_NPV(Elements, before_gc_elements - after_gc_elements)
                                                            << LOG_NPV(Bytes, before_gc_memory - after_gc_memory));

  DOUT("After Sweep" << sys::eofl);
  //_allocator->dumpStatistics(false);
  //_allocator->_heap = 0;
  return after_gc_elements != before_gc_elements;
  return false;
}

void unmarkAndUnvisitedAll(AllocatorObjectsIterator* iterator)
{
  acdk::lang::Object* o = 0;
  while ((o = iterator->getNextObject()) != 0)
  {
    o->_setObjectRefFlag(false, ObjectBase::Marked);
    o->_setObjectRefFlag(false, ObjectBase::Visited);
  }
}

void markSubStrings(String* s)
{
  if (s->storageType() != SubSST)
    return;
  s->_getSubstrBase()->_setObjectRefFlag(true, ObjectBase::Marked);
}



void markMemberObjects(AllocatorObjectsIterator* iterator)
{
  unmarkAndUnvisitedAll(iterator);
  iterator->reset();
  acdk::lang::Object* o = 0;
  while ((o = iterator->getNextObject()) != 0)
  {
    markMembers(o);
  }
  iterator->reset();
  while ((o = iterator->getNextObject()) != 0)
  {
    String* s = dynamic_cast<String*>(o);
    if (s != 0)
      markSubStrings(s);
    if (o->isStaticRef() == true)
    {
      o->_setObjectRefFlag(true, ObjectBase::Marked);
    }
  }
}

void markMembers(acdk::lang::Object* obj)
{
  //if (obj->_isObjectRefFlag(ObjectBase::Visited) == true)
  //  return;
  if (hasColletableFields(obj) == false)
    return;
  FieldReferences fields;
  obj->getCollectableFields(fields);
  for (int i = 0; i < fields.size(); i++) 
  {
    acdk::lang::Object* f = fields[i]->impl();
    if (f != 0/* && f->_isObjectRefFlag(ObjectBase::Marked) == false*/)
    {
      if (f->_isObjectRefFlag(ObjectBase::Marked) == false)
      {
        f->_setObjectRefFlag(true, ObjectBase::Marked);
        markMembers(f);
        //f->_setObjectRefFlag(true, ObjectBase::Visited);
      }
  
    }
  }
}


void listGcAble(HeapFrame* frame, AllocatorObjectsIterator* iterator, ::acdk::lang::ref::NotifyObjectEventListener* listener, int flags)
{
    acdk::lang::Object* o = 0;
  int objectcount = 0;
  while ((o = iterator->getNextObject()) != 0)
  {
    o->_setObjectRefFlag(false, ObjectBase::Marked);
    o->_setObjectRefFlag(false, ObjectBase::Visited);
    if (o->_isObjectRefFlag(ObjectBase::IsStaticRef) == true || 
        o->_isObjectRefFlag(ObjectBase::IsMemLocked) == true || 
        o->_isObjectRefFlag(ObjectBase::IsStackRef) == true //|| 
        //o->_isObjectRefFlag(ObjectBase::ObjectHasLocalGc) == true
        )
    {
      markAsExternal(o);
    }
    ++objectcount;
  }
  iterator->reset();
  ReferedMap refmap((1 + objectcount) * 3);
  
  //HolderMap holdermap(mapsize);
  FieldReferences fields;
  
  // building backward ref
  while ((o = iterator->getNextObject()) != 0)
  {
    o = iterator->getCurrentElement().mem.obj;
    if (hasColletableFields(o) == false) 
      continue;
    
    fields.clear();
    o->getCollectableFields(fields);
    for (int i = 0; i < fields.size(); i++) 
    {
      if (*fields[i] != Nil) 
      {
              //DOUT(fields[i]->impl() << " refered by " <<  o << sys::eofl);
        refmap.addRefered(fields[i]->impl(), o);
        refmap.addHolder(fields[i]);
          //holdermap.add(fields[i]);
      }
    }
  }
  //DOUT("[GC] marking" << sys::eofl);
  {
    ReferedMap::iterator it = refmap.begin();
    ReferedMap::iterator end = refmap.end();
    for (; it != end; ++it) 
    {
      acdk::lang::Object* robj = (*it).first;
      if (robj->magic() != _MagicObjectSig) 
       continue;

      if (hasExternalReference(robj, robj, refmap) == true) 
      {
        //DOUT("MARK: " << (void*)robj);
        robj->_setObjectRefFlag(true, ObjectBase::Marked);
      }
      
    }
  }
  ReferedMap::iterator it = refmap.begin();
  ReferedMap::iterator end = refmap.end();
    
  for (; it != end; ++it) 
  {
    acdk::lang::Object* obj = (*it).first;
    if (obj == 0)
      continue;
    if (obj->magic() != _MagicObjectSig) 
      continue;
    //sys::coreout << "o:" << (void*)obj << sys::eofl;
    try {
      obj->testIntegrity();
    } 
    catch (...) 
    {
      ACDK_NLOG("acdk.lang.gc", Error, "acdk::lang::Object is insane: " + Integer::toHexString((int)(void*)obj));
      return;
    }
    if (obj->_isObjectRefFlag(ObjectBase::Marked) == false) 
    {
      ObjectRefPtrVector& refvec = (*it).second->hold;
      bool restart = false;
      for (int i = 0; i < refvec.size(); i++) 
      {
        acdk::lang::Object* ref = refvec[i];
        if (ref == 0 || ref->impl() == 0)
          continue;
        if (ref->impl()->magic() != _MagicObjectSig) 
          continue;
        //DOUT("GC: Free Ref: " << (void*)ref << "; Object: " << (void*)ref->impl() << ", " << ref->impl()->getClazzInfo()->name << sys::eofl);
        if (listener->listedAllocated(frame, (*ref).impl(), ObjectMem, 0) == false)
          return;
      }
    }
  }
}

void genericListObject(HeapFrame* frame, AllocatorObjectsIterator* iterator, ::acdk::lang::ref::NotifyObjectEventListener* listener, int flags)
{
  
  acdk::lang::Object* o = 0;
  if (flags & ListRootsOnly)
  {
    markMemberObjects(iterator);
    
  }
  if (flags & ListGcAble)
  {
    listGcAble(frame, iterator, listener, flags);
    return;
  }
  iterator->reset();
  while (iterator->getNext() != 0)
  {
    AllocatorElement el = iterator->getCurrentElement();
    if (el.type == ObjectMem)
      o = el.mem.obj;
    else
      o = 0;
    if (flags & ListRootsOnly)
    {
      if (o != 0 && o->_isObjectRefFlag(ObjectBase::Marked) == false)
      {
        if (listener->listedAllocated(frame, el.mem.buffer, (AllocatedType)(AllocatedTypeMask & el.type), el.size) == false)
          return;
      }
    }
    else if (flags & ListGcAble)
    {
      if (o != 0 && o->_isObjectRefFlag(ObjectBase::Marked) == false)
      {
        if (listener->listedAllocated(frame, el.mem.buffer, (AllocatedType)(AllocatedTypeMask & el.type), el.size) == false)
          return;
      }
    }
    else
    {
      if (listener->listedAllocated(frame, el.mem.buffer, (AllocatedType)(AllocatedTypeMask & el.type), el.size) == false)
        return;
    }
  }
}



} // namespace sys
} // namespace lang 
} // namespace acdk