This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Bug when throwing exception
- To: egcs-bugs at cygnus dot com
- Subject: Bug when throwing exception
- From: "Kevin S. Van Horn" <kvanhorn at fonix dot com>
- Date: Fri, 29 Jan 1999 20:49:14 +0000
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;
}