2005/5/9

     
 

PagedAllocator.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/PagedAllocator.cpp,v 1.36 2005/04/28 15:00:05 kommer Exp $

#include <acdk.h>

#include "PagedAllocator.h"
#include "PagedHeap.h"
#include <acdk/lang/sys/core_vector.h>
#include <acdk/lang/sys/core_hashmap.h>
#include <acdk/lang/OutOfMemoryError.h>

#include <acdk/lang/ref/NotifyObjectEvent.h>

#include "core_memtrace.h"


namespace acdk {
namespace lang {
namespace sys {

using namespace acdk::lang;

// don't memset in debug mode
#define ACDK_NOMEMINITIALIZE

//#define DEBUG_ALLOC
#ifdef DEBUG_ALLOC

#define DOUT(m) \
sys::coreout << m << sys::eofl

#else
/FONT>
#define DOUT(m)
#endif
/FONT>

#undef THROW_INTERNAL
#define THROW_INTERNAL(msg) \
do {\
sys::coreout << msg << sys::eofl; \
char* nptr = 0; \
  *nptr = 0; \
} while (false)

PageMutex& _pageLock()
{
  static PageMutex _pageLock;
  return _pageLock;
}

PagedAllocator::PagedAllocator(int size, int bufferedPages)
: AbstractAllocator(ManualGcAllocatorType | SupportListObjectAllocatorType, core_string("PagedAllocator", false))
, _standardPageSize(aligned(size))
, _first(0)
, _last(0)
, _reservedPages(0)
, _bufferedPages(0, bufferedPages, 0)
, _maxBufferedPages(bufferedPages)
, _heap(0)
{
  DOUT("PagedAllocator::PagedAllocator(" << (void*)this << ")");
  _first = _last = _allocatePage(_standardPageSize);
  _first->_prev = _first;
  _first->_next = _first;
  //_maxBufferedPages = 0;
//  _bufferedPages.ensureCapacity(_maxBufferedPages);
}

PagedAllocator::~PagedAllocator()
{
  DOUT("PagedAllocator::~PagedAllocator(" << (void*)this << ")");
  doGc(true, true);

}



RawAllocator* _rawAllocator();




bool
PagedAllocator::doGc(bool threadStorage, bool force)
{
  if (force == false)
    return false;
  bool ret = false;
  while (_bufferedPages.empty() == false)
  {
    PagedAllocatorPage* page = _bufferedPages.back();
    _bufferedPages.pop_back();
    os_dealloc(page->_size, page);
    ret = true;
  }
  if (ret == true)
    return true;
  {
    PAGEUNLOCK();
    return ObjectHeap::gc(true);
  }
}





void*
PagedAllocator::allocate(int size, short type)
{
  PAGELOCK();
  return _allocate(size, type);
}

void*
PagedAllocator::_allocate(int size, short type)
{
  DOUT("PagedAllocator::_allocate(" << (void*)this  << "; size=" << size << "; type=" << type << ")");
  size = aligned(size);
  if (_last->_hasCapacity(size + PagedAllocatorPageSlot::getPagedAllocatorPageSlotSize()) == true)
    return _last->_allocate(size, type);

  int npagesize = size + ALIGNEDSIZEOF(PagedAllocatorPage) + PagedAllocatorPageSlot::getPagedAllocatorPageSlotSize();
  if (npagesize < _standardPageSize)
    npagesize = _standardPageSize;
  PagedAllocatorPage* np = _allocatePage(npagesize);
  _last = np;
  return _last->_allocate(size, type);


}

//virtual
void*
PagedAllocator::allocate(size_t size, AllocatedType at)
{
  PAGELOCK();
  addRef();
  ++_allocInfo.memTypes[at].count;
  _allocInfo.memTypes[at].size += size;
  void *ret = _allocate(aligned(size) + getHeaderSize(), at);
  MemChunkHeader* ch = getHeaderFromChunk(ret);
  ch->allocator = this;
  ch->chunkSize = aligned(size) + getHeaderSize();
  void* retptr = getObjectFromChunk(ret);
  initObjectPtr(retptr, at);
  return core_memtrace::OnAlloc(retptr, size, 3, at);
}



PagedAllocatorPage*
PagedAllocator::_allocatePage2(int size)
{
  DOUT("PagedAllocator::_allocPage2(" << (void*)this << "): new page: " << size);
  for (core_stack<PagedAllocatorPage*>::iterator it = _bufferedPages.begin();
       it != _bufferedPages.end();
       ++it)
  {
    if ((*it)->_size >= size)
    {
      PagedAllocatorPage* pap = *it;
      int psize = pap->_size;
      _bufferedPages.erase(it);
      DOUT("reuse page: " << (void*) pap << ": " << psize << "; req: " << size);
      return new (pap) PagedAllocatorPage(this, psize, 0, _last, _first);
    }
  }
  return new (os_alloc(size)) PagedAllocatorPage(this, size, 0, _last, _first);
}

PagedAllocatorPage*
PagedAllocator::_allocatePage(int size)
{
  if (size == 0)
    size = _standardPageSize;
  return _allocatePage2(aligned(size));
}


void
PagedAllocator::_deallocatePage2(PagedAllocatorPage* page)
{
  DOUT("PagedAllocator::_deallocPage2(" << (void*)this << ")");

  if (_bufferedPages.size() >= _maxBufferedPages)
  {
    PagedAllocatorPage* page_to_free = page;
    for (core_stack<PagedAllocatorPage*>::iterator it = _bufferedPages.begin();
       it != _bufferedPages.end();
       ++it)
    {
      if ((*it)->_size < page_to_free->_size)
      {
        PagedAllocatorPage* sw = *it;
        *it = page_to_free;
        page_to_free = sw;
      }
    }
    os_dealloc(page_to_free->_size, page_to_free);
    return;
  }
  DOUT("PagedAllocator::_deallocPage2(" << (void*)this << "): buffer page: " << (void*)page << ": " << page->_size);
  _bufferedPages.push_back(page);
}

void
PagedAllocator::_deallocatePage(PagedAllocatorPage* page)
{
  if (page->_magic != (short)PagedAllocatorPage::Magic)
    THROW_INTERNAL("Corrupt Allocation state");
  page->_first = 0;
  page->_last = 0;

  page->_prev->_next = page->_next;
  page->_next->_prev = page->_prev;
  if (page == _first && page == _last) // leave at least 1 Page
    return;
  if (_last == page)
    _last = page->_prev;
  if (_first == page)
    _first = page->_next;

  _deallocatePage2(page);
}


//virtual
void
PagedAllocator::deallocate(void* ptr, AllocatedType at)
{
  PAGELOCK();
  DOUT("PagedAllocator::deallocate(" << (void*)this << ", " << ptr << ")");
  core_memtrace::OnFree(ptr, 4);
  --_allocInfo.memTypes[at].count;

  //char* o = (char*)ptr;
  if (_heap != 0 && at == ObjectMem)
    _heap->onDestroy((acdk::lang::Object*)ptr);
  MemChunkHeader* ch = getHeaderFromObject(ptr);
  ptr = getChunkFromObject(ptr);

  PagedAllocatorPageSlot* ps = (PagedAllocatorPageSlot*) ((char*)ptr - ALIGNEDSIZEOF(PagedAllocatorPageSlot));
  if (ps->_magic != (short)PagedAllocatorPageSlot::Magic)
  {
    THROW_INTERNAL("PagedAllocator::deallocate() PagedAllocatorPageSlot is currupt");
    return ;
  }
  _allocInfo.memTypes[at].size -= ps->getUserMemSize();
  ps->_deallocate();
  releaseRef();
}

void*
PagedAllocator::_allocateObject(int size)
{
  return _allocate(size, ObjectMem);
}



void
PagedAllocator::_deallocateObject(void* ptr)
{
  PagedAllocatorPageSlot* ps = (PagedAllocatorPageSlot*) ((char*)ptr - ALIGNEDSIZEOF(PagedAllocatorPageSlot));
  if (ps->_magic != (short)PagedAllocatorPageSlot::Magic) {
    THROW_INTERNAL("PagedAllocator PagedAllocatorPageSlot is corrupt");
  }
  ps->_deallocate();

}


PagedAllocatorPageSlot::PagedAllocatorPageSlot(PagedAllocatorPage* page, int size, PagedAllocatorPageSlotFlags flags,
                                               PagedAllocatorPageSlot* prev,  PagedAllocatorPageSlot* next)
: _magic((short)Magic)
, _flags(flags)
, _size(size + getPagedAllocatorPageSlotSize())
, _startPage(page)
, _prev(prev)
, _next(next)
{
  if (_prev != 0)
    _prev->_next = this;
  if (_next != 0)
    _next->_prev = this;

#if defined(ACDK_DEBUG) && !defined(ACDK_NOMEMINITIALIZE)
  char* nml = getNoMenLand();
  acdk::lang::Object* obj = const_cast<PagedAllocatorPageSlot*>(this)->_getObject();
  int ums = getUserMemSize();
  memset(nml, _noMenLandMask, _noMenLandSize);
    //checkNoMenLand();
#endif
/FONT>
}


void
PagedAllocatorPageSlot::checkNoMenLand() const
{
  char* ptr = getNoMenLand();
  acdk::lang::Object* obj = const_cast<PagedAllocatorPageSlot*>(this)->_getObject();

  for (int i = 0; i < _noMenLandSize; ++i)
  {
    if (ptr[i] != _noMenLandMask)
    {
      sys::coreout << "bounds read PAPS: Page: " << (void*)this << ", size: " << _size
                <<  "; Object: " << (void*)obj << ", size: " << PagedAllocatorPageSlot::getUserMemSize() << sys::eofl;
      return;
      //THROW_INTERNAL("PagedAllocator PagedAllocatorPageSlot was corrupted by user object (bounds read)");
    }
  }
  /*sys::coreout << "ok Page: " << (void*)this << ", size: " << _size
                <<  "; Object: " << (void*)obj << ", size: " << PagedAllocatorPageSlot::getUserMemSize() << sys::eofl;*/
}

void
PagedAllocatorPageSlot::_deallocate()
{
#if defined(ACDK_DEBUG) && !defined(ACDK_NOMEMINITIALIZE)
  checkNoMenLand();
#endif
/FONT>
  //if (isObject() == true)
  //  sys::coreout << "PA-[" << (void*)getObject() << "]"  << sys::eofl;
  if (_next == 0 || _prev == this) {
    _deallocatePage();
    return;
  }
  _startPage->_deallocateSlot(this);
  _next->_prev = _prev;
  _prev->_next = _next;
  _magic = 0xdd;
#if defined(ACDK_DEBUG) && !defined(ACDK_NOMEMINITIALIZE)
  memset(((char*)this) +  ALIGNEDSIZEOF(PagedAllocatorPageSlot*), 0xdd, _size - ALIGNEDSIZEOF(PagedAllocatorPageSlot*));
#endif
/FONT>
  //memset(this, 0, _size);
}


//virtual
void*
PagedAllocator::raw_allocate(size_t size, AllocatedType type)
{
  THROW1(Error, "Misuse of PagedAllocator: raw_allocate() should never been called");
  return 0;
}

//virtual
void
PagedAllocator::raw_deallocate(size_t size, void* ptr, AllocatedType type)
{
  THROW1(Error, "Misuse of PagedAllocator: raw_allocate() should never been called");
}

sys::core_output& operator<<(sys::core_output& os, acdk::lang::Object* obj)
{
  //os << "acdk::lang::Object=[" << (void*)obj << "]; RefCount=[" << obj->refCount() << "]; ";
  os << "CLASS=[" << obj->getClazzInfo()->name << "]; ";
  return os;
}


void printindent(int level)
{
  while (level--)
    sys::coreout << "\t";
}


void
dumpMembers(acdk::lang::Object* so, acdk::lang::Object* o, int ident, int level)
{

  //int colfields = o->getClazzInfo()->getCollectableFieldsCount();
  //if (colfields == 0)
  //  return;
  FieldReferences fields;
  o->getCollectableFields(fields);
  for (int i = 0; i < fields.size(); i++)
  {
    if (fields[i]->impl() != 0)
    {
      acdk::lang::Object* fo = fields[i]->impl();
      printindent(ident);
      if (so == o)
      {
        sys::coreout << "SelfRef: " << fo << sys::eofl;
        return;
      }
      else
        sys::coreout << fo << sys::eofl;
      if (level - 1 > 0)
        dumpMembers(so, fo, ident + 1, level - 1);
    }
  }
}

void
PagedAllocator::dumpStatistics(bool withDetails, int level, bool onlyumarked)
{
  PAGELOCK();
  PagedAllocatorPage* np = _first;
  int pagecount = 0;
  int slotcount = 0;
  int objcount = 0;
  int rawcount = 0;
  int allocatedMem = 0;
  do
  {
    if (np == 0)
      break;
    if (np->_magic != (short)PagedAllocatorPage::Magic)
    {
      sys::coreout << "*** Opps, invalide PagedAllocatorPage at " << (void*)np << sys::eofl;
      THROW_INTERNAL("Memory Error");
      break;
    }
    PagedAllocatorPageSlot* ps = np->_first;
    do
    {
      if (ps == 0)
        break;
      if (ps->_magic != (short)PagedAllocatorPageSlot::Magic)
      {
        sys::coreout << "*** Opps, invalide PagedAllocatorPageSlot at " << (void*)ps << sys::eofl;
        THROW_INTERNAL("Memory Error");
        break;
      }
      if (ps->_flags & ObjectMem)
      {
        ++objcount;
        acdk::lang::Object* o = ps->_getObject();
        if (o->magic() != _MagicObjectSig)
        {
          ps = ps->_next;
          continue;
        }
        else
        {
          if (withDetails == true)
          {
            if (onlyumarked == false || o->_isObjectRefFlag(ObjectBase::Marked) == false)
            {
              sys::coreout << "P["  << np << "] S[" << ps << "] O[" << ps->mem()
                << "] size[" << ps->_size << "]; [" << o << "];"
                << "FM[" << o->_isObjectRefFlag(ObjectBase::Marked) << "]; FV[" << o->_isObjectRefFlag(ObjectBase::Visited) << "]"
                << sys::eofl;
              if (level > 0)
                dumpMembers(0, o, 1, level);
            }

          }
        }
      }
      else
      {
        ++rawcount;
        if (withDetails == true)
        {
          sys::coreout << "P["  << np << "] S[" << ps << "] R[" << ps->mem()
                << "] size[" << ps->_size << "]" << sys::eofl;
        }
      }

      ++slotcount;
      ps = ps->_next;

    } while (ps != np->_first);

    ++pagecount;
    allocatedMem += np->_size;
    np = np->_next;
  } while (np != _first);
#if 0
  sys::coreout << "PAGES=[" << pagecount << "]; ALLOC=[" << allocatedMem << "]; SLOTS=["
        << slotcount << "]; OBJ=[" << objcount << "]; RAW=[" << rawcount << "];" << sys::eofl;
#endif
/FONT>

}


int
PagedAllocator::getElementCount(AllocatedType type) const
{
  PagedAllocatorPage* np = _first;
  int elementcount = 0;
  do {
    if (np == 0)
      break;
    if (np->_magic != (short)PagedAllocatorPage::Magic) {
      sys::coreout << "*** Opps, invalide PagedAllocatorPage at " << (void*)np << sys::eofl;
      THROW_INTERNAL("Memory Error");
    }
    PagedAllocatorPageSlot* ps = np->_first;
    do {
      if (ps == 0)
        break;
      if (ps->_magic != (short)PagedAllocatorPageSlot::Magic)
      {
        sys::coreout << "*** Opps, invalide PagedAllocatorPageSlot at " << (void*)ps << sys::eofl;
        THROW_INTERNAL("Memory Error");
      }
      if ((type == UnspecifiedMem) || (type & ps->_flags))
        ++elementcount;
      ps = ps->_next;
    } while (ps != np->_first);
    np = np->_next;
  } while (np != _first);
  return elementcount;
}


int
PagedAllocator::getAllocatedCount(AllocatedType type) const
{
  PAGELOCK();
  PagedAllocatorPage* np = _first;
  int elementcount = 0;
  do {
    if (np == 0)
      break;
    if (np->_magic != (short)PagedAllocatorPage::Magic) {
      sys::coreout << "*** Opps, invalide PagedAllocatorPage at " << (void*)np << sys::eofl;
      THROW_INTERNAL("Memory Error");
    }
    PagedAllocatorPageSlot* ps = np->_first;
    do {
      if (ps == 0)
        break;
      if (ps->_magic != (short)PagedAllocatorPageSlot::Magic) {
        sys::coreout << "*** Opps, invalide PagedAllocatorPageSlot at " << (void*)ps << sys::eofl;
        THROW_INTERNAL("Memory Error");
      }
      if ((type == UnspecifiedMem) || (type & ps->_flags))
        elementcount += ps->_size;
      ps = ps->_next;
    } while (ps != np->_first);
    np = np->_next;
  } while (np != _first);
  return elementcount;
}

//virtual
void
PagedAllocator::listObjects(::acdk::lang::ref::NotifyObjectEventListener* listener, int flags)
{
  PagedAllocatorPage* np = _first;
  int elementcount = 0;
  do {
    if (np == 0)
      break;
    if (np->_magic != (short)PagedAllocatorPage::Magic) {
      sys::coreout << "*** Opps, invalide PagedAllocatorPage at " << (void*)np << sys::eofl;
      THROW_INTERNAL("Memory Error");
    }
    PagedAllocatorPageSlot* ps = np->_first;
    do {
      if (ps == 0)
        break;
      if (ps->_magic != (short)PagedAllocatorPageSlot::Magic) {
        sys::coreout << "*** Opps, invalide PagedAllocatorPageSlot at " << (void*)ps << sys::eofl;
        THROW_INTERNAL("Memory Error");
      }

      if (listener->listedAllocated(0, ps->mem(), AllocatedType(ps->_flags), ps->_size) == false)
        return;

      ps = ps->_next;
    } while (ps != np->_first);
    np = np->_next;
  } while (np != _first);
}


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