/* Test the allocation error recovery facility of the library.
   Copyright (C) 2001-2004 Roberto Bagnara <bagnara@cs.unipr.it>

This file is part of the Parma Polyhedra Library (PPL).

The PPL 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.

The PPL 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

For the most up-to-date information see the Parma Polyhedra Library
site: http://www.cs.unipr.it/ppl/ . */

#include "ppl_test.hh"
#include <new>
#include <cstring>
#include <cerrno>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

using namespace std;
using namespace Parma_Polyhedra_Library;

#ifndef NOISY
#define NOISY 0
#endif

// If GMP does not support exceptions the test is pointless.
// Cygwin has an almost dummy definition of setrlimit().
#if !GMP_SUPPORTS_EXCEPTIONS \
|| defined(__CYGWIN__) \
|| !(HAVE_DECL_RLIMIT_DATA || HAVE_DECL_RLIMIT_RSS \
     || HAVE_DECL_RLIMIT_VMEM || HAVE_DECL_RLIMIT_AS)

int
main() TRY {
  return 0;
}
CATCH

#else

static void
compute_open_hypercube_generators(dimension_type dimension) {
  NNC_Polyhedron hypercube(dimension);
  for (dimension_type i = 0; i < dimension; ++i) {
    Variable x(i);
    hypercube.add_constraint(x > 0);
    hypercube.add_constraint(x < 1);
  }
  (void) hypercube.generators();
}

#define LIMIT(WHAT) \
do { \
  if (getrlimit(WHAT, &t) != 0) { \
    cerr << "getrlimit failed: " << strerror(errno) << endl; \
    exit(1); \
  } \
  t.rlim_cur = bytes; \
  if (setrlimit(WHAT, &t) != 0) { \
    cerr << "setrlimit failed: " << strerror(errno) << endl; \
    exit(1); \
  } \
} while(0)

static void
limit_memory(unsigned long bytes) {
  struct rlimit t;
#if HAVE_DECL_RLIMIT_DATA
  // Limit heap size.
  LIMIT(RLIMIT_DATA);
#endif
#if HAVE_DECL_RLIMIT_RSS
  // Limit resident set size.
  LIMIT(RLIMIT_RSS);
#endif
#if HAVE_DECL_RLIMIT_VMEM
  // Limit mapped memory (brk + mmap).
  LIMIT(RLIMIT_VMEM);
#endif
#if HAVE_DECL_RLIMIT_AS
  // Limit virtual memory.
  LIMIT(RLIMIT_AS);
#endif
}

static bool
guarded_compute_open_hypercube_generators(dimension_type dimension,
					  unsigned long max_memory_in_bytes) {
  try {
    limit_memory(max_memory_in_bytes);
    compute_open_hypercube_generators(dimension);
    return true;
  }
  catch (const bad_alloc&) {
#if NOISY
    cout << "out of virtual memory" << endl;
#endif
    return false;
  }
  catch (...) {
    exit(1);
  }
  // Should never get here.
  return false;
}

extern "C" void*
cxx_malloc(size_t size) {
  return ::operator new(size);
}

extern "C" void*
cxx_realloc(void* p, size_t old_size, size_t new_size) {
  if (new_size <= old_size)
    return p;
  else {
    void* new_p = ::operator new(new_size);
    memcpy(new_p, p, old_size);
    ::operator delete(p);
    return new_p;
  }
}

extern "C" void
cxx_free(void* p, size_t) {
  ::operator delete(p);
}

#define INIT_MEMORY 3*1024*1024

int
main() TRY {
  mp_set_memory_functions(cxx_malloc, cxx_realloc, cxx_free);

  set_handlers();

  // Find a dimension that cannot be computed with INIT_MEMORY bytes.
  dimension_type dimension = 0;
  do {
    ++dimension;
#if NOISY
    cout << "Trying dimension " << dimension << endl;
#endif
  }
  while (guarded_compute_open_hypercube_generators(dimension, INIT_MEMORY));

  // Now find an upper bound to the memory necessary to compute it.
  unsigned long upper_bound = INIT_MEMORY;
  do {
    upper_bound *= 2;
#if NOISY
    cout << "Trying upper bound " << upper_bound << endl;
#endif
  }
  while (!guarded_compute_open_hypercube_generators(dimension, upper_bound));

  // Search the "exact" amount of memory.
  int lower_bound = upper_bound/2;
  do {
    int test_memory = (lower_bound+upper_bound)/2;
#if NOISY
    cout << "Probing " << test_memory << endl;
#endif
    if (guarded_compute_open_hypercube_generators(dimension, test_memory))
      upper_bound = test_memory;
    else
      lower_bound = test_memory;
  } while (upper_bound-lower_bound > 1024);

#if NOISY
  cout << "Estimated memory for dimension " << dimension
       << ": " << (lower_bound+upper_bound)/2 << " bytes" << endl;
#endif

  return 0;
}
CATCH

#endif // GMP_SUPPORTS_EXCEPTIONS && !defined(__CYGWIN__) && ...


syntax highlighted by Code2HTML, v. 0.9.1