Loading...
Searching...
No Matches
mm.cpp
Go to the documentation of this file.
/****************************************************************************
*
* MODULE: iostream
*
* COPYRIGHT (C) 2007 Laura Toma
*
*
* Iostream is a library that implements streams, external memory
* sorting on streams, and an external memory priority queue on
* streams. These are the fundamental components used in external
* memory algorithms.
* Credits: The library was developed by Laura Toma. The kernel of
* class STREAM is based on the similar class existent in the GPL TPIE
* project developed at Duke University. The sorting and priority
* queue have been developed by Laura Toma based on communications
* with Rajiv Wickremesinghe. The library was developed as part of
* porting Terraflow to GRASS in 2001. PEARL upgrades in 2003 by
* Rajiv Wickremesinghe as part of the Terracost project.
*
* 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. *
* **************************************************************************/
// A simple registration based memory manager.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
// #include <mm.h>
#include <grass/iostream/mm.h>
#define MM_DEBUG if (0)
/* ************************************************************ */
MM_register::MM_register()
{
instances++;
if (instances > 1) {
cerr << "MM_register(): Only 1 instance of MM_register should exist.\n";
assert(0); // core dump if debugging
exit(1);
}
assert(instances == 1);
// by default, we ignore if memory limit is exceeded
register_new = MM_IGNORE_MEMORY_EXCEEDED;
}
/* ************************************************************ */
MM_register::~MM_register(void)
{
if (instances > 1) {
cerr << "MM_register(): Only 1 instance of MM_register should exist.\n";
assert(0); // core dump if debugging
exit(1);
}
assert(instances == 1);
instances--;
}
/* ************************************************************ */
void MM_register::print()
{
size_t availMB = (remaining >> 20);
if (remaining) {
cout << "available memory: " << availMB << "MB "
<< "(" << remaining << "B)" << endl;
}
else {
cout << "available memory: " << remaining
<< "B, exceeding: " << used - user_limit << "B" << endl;
}
}
/* ************************************************************ */
// User-callable method to set allowable memory size
MM_err MM_register::set_memory_limit(size_t new_limit)
{
assert(new_limit > 0);
if (used > new_limit) {
// return MM_ERROR_EXCESSIVE_ALLOCATION;
switch (register_new) {
case MM_ABORT_ON_MEMORY_EXCEEDED:
cerr << " MM_register::set_memory_limit to " << new_limit
<< ", used " << used << ". allocation exceeds new limit.\n";
cerr.flush();
assert(0); // core dump if debugging
exit(1);
break;
case MM_WARN_ON_MEMORY_EXCEEDED:
cerr << " MM_register::set_memory_limit to " << new_limit
<< ", used " << used << ". allocation exceeds new limit.\n";
break;
case MM_IGNORE_MEMORY_EXCEEDED:
break;
}
user_limit = new_limit;
remaining = 0;
return MM_ERROR_NO_ERROR;
}
assert(used <= new_limit);
// These are unsigned, so be careful.
if (new_limit < user_limit) {
remaining -= user_limit - new_limit;
}
else {
remaining += new_limit - user_limit;
}
user_limit = new_limit;
return MM_ERROR_NO_ERROR;
}
/* ************************************************************ */
// only warn if memory limit exceeded
void MM_register::warn_memory_limit()
{
register_new = MM_WARN_ON_MEMORY_EXCEEDED;
}
/* ************************************************************ */
// abort if memory limit exceeded
void MM_register::enforce_memory_limit()
{
register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
if (used > user_limit) {
cerr << " MM_register::enforce_memory_limit: limit=" << user_limit
<< ", used=" << used << ". allocation exceeds limit.\n";
assert(0); // core dump if debugging
exit(1);
}
}
/* ************************************************************ */
// ignore memory limit accounting
void MM_register::ignore_memory_limit()
{
register_new = MM_IGNORE_MEMORY_EXCEEDED;
}
/* ************************************************************ */
// provide accounting state
MM_mode MM_register::get_limit_mode()
{
return register_new;
}
/* ************************************************************ */
// provide print ccounting state
void MM_register::print_limit_mode()
{
cout << "Memory manager registering memory in ";
switch (register_new) {
case MM_ABORT_ON_MEMORY_EXCEEDED:
cout << "MM_ABORT_ON_MEMORY_EXCEEDED";
break;
case MM_WARN_ON_MEMORY_EXCEEDED:
cout << "MM_WARN_ON_MEMORY_EXCEEDED";
break;
case MM_IGNORE_MEMORY_EXCEEDED:
cout << "MM_IGNORE_MEMORY_EXCEEDED";
break;
}
cout << " mode." << endl;
}
/* ************************************************************ */
// return the amount of memory available before user-specified memory
// limit will be exceeded
size_t MM_register::memory_available()
{
return remaining;
}
/* ************************************************************ */
size_t MM_register::memory_used()
{
return used;
}
/* ************************************************************ */
size_t MM_register::memory_limit()
{
return user_limit;
}
/* ---------------------------------------------------------------------- */
// return the overhead on each memory allocation request
// SIZE_SPACE is to ensure alignment on quad word boundaries. It may be
// possible to check whether a machine needs this at configuration
// time or if dword alignment is ok. On the HP 9000, bus errors occur
// when loading doubles that are not qword aligned.
static const size_t SIZE_SPACE = (sizeof(size_t) > 8 ? sizeof(size_t) : 8);
int MM_register::space_overhead()
{
return SIZE_SPACE;
}
/* ************************************************************ */
// check that new allocation request is below user-defined limit.
// This should be a private method, only called by operator new.
MM_err MM_register::register_allocation(size_t request)
{
if (request > remaining) {
remaining = 0;
used += request;
return MM_ERROR_INSUFFICIENT_SPACE;
}
else {
used += request;
remaining -= request;
return MM_ERROR_NO_ERROR;
}
}
/* ************************************************************ */
// do the accounting for a memory deallocation request.
// This should be a private method, only called by operators
// delete and delete [].
MM_err MM_register::register_deallocation(size_t sz)
{
if (sz > used) {
used = 0;
remaining = user_limit;
return MM_ERROR_UNDERFLOW;
}
else {
used -= sz;
if (used < user_limit) {
remaining = user_limit - used;
}
else {
assert(remaining == 0);
}
return MM_ERROR_NO_ERROR;
}
}
/* ************************************************************ */
/* these overloaded operators must only be used by this memory manager
* risk of invalid free if these operators are defined outside the MM_register
* class e.g. GDAL allocating memory with something else than new as defined
* here but then using delete as defined here
*/
#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
void *MM_register::operator new[](size_t sz) throw(std::bad_alloc)
{
#else
void *MM_register::operator new[](size_t sz)
{
#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
void *p;
MM_DEBUG cout << "new: sz=" << sz << ", register " << sz + SIZE_SPACE
<< "B ,";
if (MM_manager.register_allocation(sz + SIZE_SPACE) != MM_ERROR_NO_ERROR) {
// must be MM_ERROR_INSUF_SPACE
switch (MM_manager.register_new) {
case MM_ABORT_ON_MEMORY_EXCEEDED:
cerr << "MM error: limit =" << MM_manager.memory_limit() << "B. "
<< "allocating " << sz << "B. "
<< "limit exceeded by "
<< MM_manager.memory_used() - MM_manager.memory_limit() << "B."
<< endl;
assert(0); // core dump if debugging
exit(1);
break;
case MM_WARN_ON_MEMORY_EXCEEDED:
cerr << "MM warning: limit=" << MM_manager.memory_limit() << "B. "
<< "allocating " << sz << "B. "
<< " limit exceeded by "
<< MM_manager.memory_used() - MM_manager.memory_limit() << "B."
<< endl;
break;
case MM_IGNORE_MEMORY_EXCEEDED:
break;
}
}
p = malloc(sz + SIZE_SPACE);
if (!p) {
cerr << "new: out of memory while allocating " << sz << "B" << endl;
assert(0);
exit(1);
}
*((size_t *)p) = sz;
MM_DEBUG cout << "ptr=" << (void *)(((char *)p) + SIZE_SPACE) << endl;
return ((char *)p) + SIZE_SPACE;
}
/* ************************************************************ */
#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
void *MM_register::operator new(size_t sz) throw(std::bad_alloc)
{
#else
void *MM_register::operator new(size_t sz)
{
#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
void *p;
MM_DEBUG cout << "new: sz=" << sz << ", register " << sz + SIZE_SPACE
<< "B ,";
if (MM_manager.register_allocation(sz + SIZE_SPACE) != MM_ERROR_NO_ERROR) {
// must be MM_ERROR_INSUF_SPACE
switch (MM_manager.register_new) {
case MM_ABORT_ON_MEMORY_EXCEEDED:
cerr << "MM error: limit =" << MM_manager.memory_limit() << "B. "
<< "allocating " << sz << "B. "
<< "limit exceeded by "
<< MM_manager.memory_used() - MM_manager.memory_limit() << "B."
<< endl;
assert(0); // core dump if debugging
exit(1);
break;
case MM_WARN_ON_MEMORY_EXCEEDED:
cerr << "MM warning: limit=" << MM_manager.memory_limit() << "B. "
<< "allocating " << sz << "B. "
<< " limit exceeded by "
<< MM_manager.memory_used() - MM_manager.memory_limit() << "B."
<< endl;
break;
case MM_IGNORE_MEMORY_EXCEEDED:
break;
}
}
p = malloc(sz + SIZE_SPACE);
if (!p) {
cerr << "new: out of memory while allocating " << sz << "B" << endl;
assert(0);
exit(1);
}
*((size_t *)p) = sz;
MM_DEBUG cout << "ptr=" << (void *)(((char *)p) + SIZE_SPACE) << endl;
return ((char *)p) + SIZE_SPACE;
}
/* ---------------------------------------------------------------------- */
#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
void MM_register::operator delete(void *ptr) throw()
{
#else
void MM_register::operator delete(void *ptr) noexcept
{
#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
size_t sz;
void *p;
MM_DEBUG cout << "delete: ptr=" << ptr << ",";
if (!ptr) {
cerr << "MM warning: operator delete was given a NULL pointer\n";
cerr.flush();
// this may actually happen: for instance when calling a default
// destructor for something that was not allocated with new
// e.g. ofstream str(name) ---- ~ofstream() called ==> ptr=NULL
// who wrote the above comment? -RW
assert(0);
// exit(1);
return;
}
assert(ptr);
/* causes invalid free if ptr has not been allocated with new as
* defined above */
p = ((char *)ptr) - SIZE_SPACE; // the base of memory
sz = *((size_t *)p);
MM_DEBUG cout << "size=" << sz << ", free " << p << "B and deallocate "
<< sz + SIZE_SPACE << endl;
if (MM_manager.register_deallocation(sz + SIZE_SPACE) !=
MM_ERROR_NO_ERROR) {
// must be MM_ERROR_UNDERFLOW
cerr << "delete: MM_manager.register_deallocation failed\n";
assert(0);
exit(1);
}
free(p);
}
/* ---------------------------------------------------------------------- */
#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
void MM_register::operator delete[](void *ptr) throw()
{
#else
void MM_register::operator delete[](void *ptr) noexcept
{
#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
size_t sz;
void *p;
MM_DEBUG cout << "delete[]: ptr=" << ptr << ",";
if (!ptr) {
// can this happen? -- it does: see delete above
cerr << "MM warning: operator delete [] was given a NULL pointer\n";
cerr.flush();
// assert(0);
// exit(1);
return;
}
assert(ptr);
/* causes invalid free if ptr has not been allocated with new as
* defined above */
p = ((char *)ptr) - SIZE_SPACE; // the base of memory
sz = *((size_t *)p);
MM_DEBUG cout << "size=" << sz << ", free " << p << "B and deallocate "
<< sz + SIZE_SPACE << endl;
if (MM_manager.register_deallocation(sz + SIZE_SPACE) !=
MM_ERROR_NO_ERROR) {
// must be MM_ERROR_UNDERFLOW
cerr << "delete[]: MM_manager.register_deallocation failed\n";
assert(0);
exit(1);
}
free(p);
}
/* ************************************************************ */
// Instantiate the actual memory manager, and allocate the
// its static data members
MM_register MM_manager;
int MM_register::instances = 0; // Number of instances. (init)
// TPIE's "register memory requests" flag
MM_mode MM_register::register_new = MM_IGNORE_MEMORY_EXCEEDED;
// This causes constructors for static variables to fail
// MM_mode MM_register::register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
/* ************************************************************ */
// The counter of mm_register_init instances. It is implicitly set to 0.
unsigned int mm_register_init::count;
// The constructor and destructor that ensure that the memory manager is
// created exactly once, and destroyed when appropriate.
mm_register_init::mm_register_init(void)
{
if (count++ == 0) {
MM_manager.set_memory_limit(MM_DEFAULT_MM_SIZE);
}
}
mm_register_init::~mm_register_init(void)
{
--count;
}
count
int count
assert
#define assert(condition)
Definition lz4.c:291
MM_DEBUG
#define MM_DEBUG
Definition mm.cpp:49
MM_manager
MM_register MM_manager
Definition mm.cpp:475