This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Bug when throwing exception


Version: egcs-2.90.27 980315 (egcs-1.0.2 release)

Platform: RedHat 5.1, Intel

Problem description:
  When an exception is thrown, apparently operator new() is called. 
This is
  a problem for anyone writing their own operator new(), since operator
new()
  may have to throw a bad_alloc exception if it can't allocate memory,
and
  the throw will then call operator new() to allocate memory for the
exception
  object, and we have an infinite regress going.

Test program:
  The problem is demonstrated by the program test_diagnew, which is
built
  as follows:

  /usr/bin/g++ -g   -c test_diagnew.cc -o test_diagnew.o
  /usr/bin/g++ -g   -c diagnew.cc -o diagnew.o
  /usr/bin/g++ -g -o test_diagnew test_diagnew.o diagnew.o 

Here are the source files:

--------------------------- nsdefines.h
-------------------------------------
#ifndef NSDEFINES_H
#define NSDEFINES_H

#ifdef USE_NAMESPACE

#define STD               std
#define KSVH              ksvh
#define SIZED_TYPES_AUX   sized_types_aux
#define SMART_PTR_AUX     smart_ptr_aux
#define BEGIN_NAMESPACE(ns)  namespace ns {
#define END_NAMESPACE        }
#define USING_NAMESPACE(ns)  using namespace ns;

#else

#define STD
#define KSVH
#define SIZED_TYPES_AUX
#define SMART_PTR_AUX

#define BEGIN_NAMESPACE(ns)
#define END_NAMESPACE
#define USING_NAMESPACE(ns)

#endif

#endif

----------------------------------- (diagnew.h)
------------------------------
#ifndef KSVH_DIAG_NEW_H
#define KSVH_DIAG_NEW_H

#include "nsdefines.h"
#include <new>

BEGIN_NAMESPACE(KSVH)

  struct memleak {
    memleak();
    ~memleak();
  private:
    unsigned long const n;
  };

  struct newfail {
    newfail(bool on = true);
    ~newfail();
  private:
    bool autofail_save;
  };

END_NAMESPACE

#endif

----------------------------------- diagnew.cc
------------------------------
#include <new>
#include <stdlib.h>
#include "diagnew.h"
#undef NDEBUG
#include <assert.h>


typedef unsigned char byte;

BEGIN_NAMESPACE( )

  const size_t align = 4; //***IMPLEMENTATION-DEPENDENT***

#define roundup(n) (((n + align - 1) / align) * align)
// HAS to be a #define instead of an inline function, because otherwise
// the variable extra_unit (below) doesn't get initialized until after
// offset and total_extra, which are defined in terms of extra_unit.

  const size_t extra_unit = roundup(sizeof(unsigned long));
  const size_t offset = 2 * extra_unit;
  const size_t total_extra = 3 * extra_unit;
  const unsigned long pattern = 0xa5a5a5a5;
  const unsigned long already_deleted = 0x5a5a5a5a;

  inline unsigned long & allocsize(void * p)
    { return *(unsigned long *)p; }
  inline unsigned long & frontguard(void * p)
    { return *(unsigned long *)((byte *)p + extra_unit); }
  inline unsigned long & backguard(void * p)
    { return *(unsigned long *)((byte *)p + offset + allocsize(p)); }

  inline void * external_pointer(void * p)
    { return (byte *)p + offset; }
  inline void * internal_pointer(void * p)
    { return (byte *)p - offset; }
  inline size_t internal_size(size_t n)
    { return total_extra + roundup(n); }

END_NAMESPACE

static unsigned long allocbytes = 0;
static bool autofail = false;

BEGIN_NAMESPACE(KSVH)

  memleak::memleak()
    : n(allocbytes) { }

  memleak::~memleak()
    { assert(allocbytes == n); }

  newfail::newfail(bool on)
    : autofail_save(autofail)
    { autofail = on; }

  newfail::~newfail()
    { autofail = autofail_save; }

END_NAMESPACE

void *operator new(size_t n, const STD::nothrow_t& x) throw()
{
  if (autofail)
    return 0;
  STD::new_handler nh = STD::set_new_handler(0);
  unsigned const N = internal_size(n);
  void * p;
  while ((p = malloc(N)) == 0 && nh)
    (*nh)();
  STD::set_new_handler(nh);
  if (p) {
    allocbytes += allocsize(p) = roundup(n);
    frontguard(p) = backguard(p) = pattern;
    return external_pointer(p);
  }
  else
    return 0;
}

void operator delete(void * q) throw()
{
  void * p = internal_pointer(q);
  assert(frontguard(p) != already_deleted);
  assert(frontguard(p) == pattern);
  assert(backguard(p) == pattern);
  frontguard(p) = already_deleted;
  allocbytes -= allocsize(p);
  free(p);
}

void *operator new(size_t n) throw(STD::bad_alloc)
{
  void * p = operator new(n, STD::nothrow_t());
  if (p)
    return p;
  else
    throw STD::bad_alloc();
}

void operator delete(void * p, const STD::nothrow_t&) throw()
{ operator delete(p); }

void *operator new[](size_t n) throw(STD::bad_alloc)
{ return operator new(n); }

void *operator new[](size_t n, const STD::nothrow_t& x) throw()
{ return operator new(n, x); }

void operator delete[](void * p) throw(STD::bad_alloc)
{ operator delete(p); }

void operator delete[](void * p, const STD::nothrow_t& x) throw()
{ operator delete(p, x); }

-------------------------------- test_diagnew.cc
----------------------------
#include "diagnew.h"
#include <iostream>
USING_NAMESPACE(STD)
USING_NAMESPACE(KSVH)

//NOTE: memleak and replacements for new and delete are tested in
//test_smart_ptr.

struct null_alloc : public STD::exception {
  virtual const char * what() const throw() {
    return "new returned null pointer";
  }
};

struct didnt_fail : public STD::exception {
  virtual const char * what() const throw() {
    return "new didn't fail";
  }
};

enum { array_new = 1, should_throw = 2, should_fail = 4 };

void do_new(unsigned flags)
{
  int * p;
  if (flags & array_new)
    if (flags & should_throw)
      p = new int[5];
    else
      p = new (nothrow) int[5];
  else
    if (flags & should_throw)
      p = new int;
    else
      p = new (nothrow) int;
  if (!p) throw null_alloc();
  if (flags & array_new)
    delete [] p;
  else
    delete p;
}

void test_new(unsigned flags)
{
  if (flags & should_fail) {
    try {
      do_new(flags);
      throw didnt_fail();
    }
    catch (STD::bad_alloc &) {
      if (!(flags & should_throw)) throw;
    }
    catch (null_alloc &) {
      if (flags & should_throw) throw;
    }
  }
  else
    do_new(flags);
}

void test(bool fail)
{
  for (unsigned i = 0; i < should_fail; ++i)
    test_new(i | (fail ? should_fail : 0));
}

int main(int argc, char * * argv)
{
  cout << argv[0] << " :" << endl;
  try {
    test(false);
    newfail dummy1;
    test(true);
    newfail dummy2(false);
    test(false);
    newfail dummy3(true);
    test(true);
  }
  catch (STD::exception & e) {
    cout << "Error: " << e.what();
    return 1;
  }
  cout << "OK" << endl;
  return 0;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]