C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

problems with constructor throwing exception

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
andrew_nuss@yahoo.com
Guest





PostPosted: Fri Sep 22, 2006 11:06 pm    Post subject: problems with constructor throwing exception Reply with quote



Hi,

I have a base class with a constructor that can throw a memory
exception. This could happen only when the last of the members is
being initialized in the initializer list. Moreover, the usage model
is to always use placement new syntax with a new operator. Also, none
of the data members in the base class nor derived class has a
destructor! I manage cleanup of resources in the destructors of the 2
classes.

I wanted to check the following assumptions when the base class causes
a memory error:

(1) neither the destructor for the base class nor the derived class are
invoked in this scenario.
(2) Since none of the data members of the base class nor derived class
have destructors, there are no destructors to invoke for these.
(3) The memory error is caused by allocating an object from a pool in
the base class, so since there is no destructor for the pool, and the
destructor of the base class is never invoked, there is a potential
leak here.
(4) The placement new operator defined for the base class also has a
placement delete operator (forced by the compiler). My guess is that
the compiler calls my placement new operator first (which gets memory
from the heap operand of the placement variable), then tries to
construct, does its own internal catch, if it fails to construct, then
it sees that no constructors were completed, so does not call any
destructors, and finally, the internal code calls the placement delete
operator to complement the placement new call.

I am especially concerned with (4). If the compiler does not
internally generate a catch clause that calls placement delete upon
encountering an exception in the base class constructor, then this is a
leak that I have no control over. Can someone explain the rollback
scenario or lack thereof for placement new, and if the compiler does
have a rollback scenario, where is this code generated? Is it
generated inline in each constructor call. If it is generated inline
in each constructor call with a catch then whereever I use:

MyDerivedClass* obj = new (myheap) MyDerivedClass();

I am not going to get any register optimizations in the body of the
code that makes the above call!

My conclusion is that I am best served by using a 2-phase construction,
in which the actual constructor cannot throw an exception, and then
there is a subsequent call made to a Construct() function.

Andy


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Alberto Ganesh Barbati
Guest





PostPosted: Sat Sep 23, 2006 7:04 pm    Post subject: Re: problems with constructor throwing exception Reply with quote



andrew_nuss (AT) yahoo (DOT) com ha scritto:
Quote:
Hi,

I have a base class with a constructor that can throw a memory
exception. This could happen only when the last of the members is
being initialized in the initializer list. Moreover, the usage model
is to always use placement new syntax with a new operator. Also, none
of the data members in the base class nor derived class has a
destructor! I manage cleanup of resources in the destructors of the 2
classes.

Why don't you just post a code snippet? No matter how hard you describe
it, people will find it difficult to visualize a problem that complex
without reading the code.

Quote:
I wanted to check the following assumptions when the base class causes
a memory error:

(1) neither the destructor for the base class nor the derived class are
invoked in this scenario.

The destructor of the base class will be invoked. The base class
sub-object is fully constructed before trying to construct the class
members, so it must be destroyed. The destructor of derived class is not
invoked.

Quote:
(2) Since none of the data members of the base class nor derived class
have destructors, there are no destructors to invoke for these.

Unless they are built-in types they *will* have destructors, as the
compiler will generate them automatically. However, implicit destructors
can be "trivial" so they won't have any effect and the compiler will
optimize them away.

Quote:
(3) The memory error is caused by allocating an object from a pool in
the base class, so since there is no destructor for the pool, and the
destructor of the base class is never invoked, there is a potential
leak here.

I'm sorry but I have completely lost you there. I can't answer reliably
without seeing the code.

Quote:
(4) The placement new operator defined for the base class also has a
placement delete operator (forced by the compiler). My guess is that
the compiler calls my placement new operator first (which gets memory
from the heap operand of the placement variable), then tries to
construct, does its own internal catch, if it fails to construct, then
it sees that no constructors were completed, so does not call any
destructors, and finally, the internal code calls the placement delete
operator to complement the placement new call.

You don't have to guess. If the constructor throws, a matching delete
operator will be called to release the memory (possibly) allocated by
the new operator.

Quote:
I am especially concerned with (4). If the compiler does not
internally generate a catch clause that calls placement delete upon
encountering an exception in the base class constructor, then this is a
leak that I have no control over.

Don't think too much. If you do your homeworks, then the compiler will
do the right thing. Everything is fully described on any good C++ book.

Quote:
leak that I have no control over. Can someone explain the rollback
scenario or lack thereof for placement new, and if the compiler does
have a rollback scenario, where is this code generated? Is it
generated inline in each constructor call. If it is generated inline
in each constructor call with a catch then whereever I use:

MyDerivedClass* obj = new (myheap) MyDerivedClass();

I am not going to get any register optimizations in the body of the
code that makes the above call!

You must be joking! First of all you should not be concerned with
register optimizations at all. For what you may know, they might not
even occur. Second, on most recent CPU architectures they provide a very
negligible gain, so what for? Third, you can't speak about dynamic
memory allocation and be concerned about low-level
micro-optimizations!!! If your constructor can conceivably throw a
memory exception then it is going to access the heap, right? Such
operation is going to be thousands of times slower than any possible
low-level optimization on parameter passing. Most probably, it will also
inhibit such optimizations.

Quote:
My conclusion is that I am best served by using a 2-phase construction,
in which the actual constructor cannot throw an exception, and then
there is a subsequent call made to a Construct() function.

First profile, then act. Premature optimization is the root of all evil.
I won't change the design (especially if that means making it worse)
in front of the possibility to lose something that might be negligible.

Regards,

Ganesh

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
peter koch larsen
Guest





PostPosted: Sat Sep 23, 2006 7:45 pm    Post subject: Re: problems with constructor throwing exception Reply with quote



andrew_nuss (AT) yahoo (DOT) com skrev:

Quote:
Hi,

I have a base class with a constructor that can throw a memory
exception. This could happen only when the last of the members is
being initialized in the initializer list. Moreover, the usage model
is to always use placement new syntax with a new operator. Also, none
of the data members in the base class nor derived class has a
destructor! I manage cleanup of resources in the destructors of the 2
classes.

I wanted to check the following assumptions when the base class causes
a memory error:

(1) neither the destructor for the base class nor the derived class are
invoked in this scenario.
(2) Since none of the data members of the base class nor derived class
have destructors, there are no destructors to invoke for these.
(3) The memory error is caused by allocating an object from a pool in
the base class, so since there is no destructor for the pool, and the
destructor of the base class is never invoked, there is a potential
leak here.
(4) The placement new operator defined for the base class also has a
placement delete operator (forced by the compiler). My guess is that
the compiler calls my placement new operator first (which gets memory
from the heap operand of the placement variable), then tries to
construct, does its own internal catch, if it fails to construct, then
it sees that no constructors were completed, so does not call any
destructors, and finally, the internal code calls the placement delete
operator to complement the placement new call.

I am especially concerned with (4). If the compiler does not
internally generate a catch clause that calls placement delete upon
encountering an exception in the base class constructor, then this is a
leak that I have no control over. Can someone explain the rollback
scenario or lack thereof for placement new, and if the compiler does
have a rollback scenario, where is this code generated? Is it
generated inline in each constructor call. If it is generated inline
in each constructor call with a catch then whereever I use:

MyDerivedClass* obj = new (myheap) MyDerivedClass();

I am not going to get any register optimizations in the body of the
code that makes the above call!

My conclusion is that I am best served by using a 2-phase construction,
in which the actual constructor cannot throw an exception, and then
there is a subsequent call made to a Construct() function.

That can never be correct. You probably have a case of bad design, but
normally you handle those cases by introducing safe intermediates - eg:

class bad
{
class foo* pf;
class bar* pb;
public: bad();
};

// dangerous way to do it:
bad::bad()
{
pf = new foo();
pb = new bar();
}


// safe way to do it:
bad::bad()
{
std::auto_ptr<foo> hpf(new foo());
std::auto_ptr<bar> hpb(new bar());
pf = hpf.release();
pb = hpb.release();
}


Quote:

Andy



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
andrew_nuss@yahoo.com
Guest





PostPosted: Sat Sep 23, 2006 9:47 pm    Post subject: Re: problems with constructor throwing exception Reply with quote

Alberto Ganesh Barbati wrote:
Quote:

Why don't you just post a code snippet? No matter how hard you describe
it, people will find it difficult to visualize a problem that complex
without reading the code.



// I am forced to take over memory management for my module by
architectural
// constraints. It shouldn't be too much slower than malloc/free.
struct Heap {

// discards all physical blocks held to create the smaller logical
blocks
~Heap ();

// throws an exception if physical memory cannot be obtained
// otherwise, creates a logical block in the heap
// more or less same api as malloc
void* Alloc (size_t size);

// more or less same api as free
void Free (void* p);
};

// policy for objects created on a heap
struct Object {
// never used, provide a throwing implementation
static void* operator new (size_t);

// always used
static void* operator new (size_t, Heap&);

// always used in program
// finds the Heap* in a hidden header
static void operator delete (void*);

// used in internal compiler code when
// the constructor throws??? This would
// imply that any new call in the program
// would generate a hidden try block,
// and I am wondering if this is inline???
static void operator delete (void*, Heap&);
};

struct Node {
...
};

struct NodePool {
NodePool (Heap&);

Node* NewNode (); // can throw

// in lieu of destructor!
// frees the pool back to the heap
void Destroy ();
};

class Graph : public Object {

Heap heap;
NodePool pool;
Node* const entry;
Node* const exit;

public:

Graph (Heap& myheap) :
heap(myheap),
pool(myheap), // can't throw
entry(pool.NewNode()), // can throw
exit(pool.NewNode()) // can throw
{
}

// will never be invoked if 2nd NewNode above
// throws, implying that 1st NewNode causes
// pool growth, but without destructor in Pool
// (by design), the pool leaks.
~Graph ()
{
// since pool has no destructor
// this is the only way it is released
pool.Destroy();
}
};

class SpecialGraph : public Graph {
//... additional pools and members

public:

SpecialGraph (Heap&);
...
};

main {
Heap heap;

// what kind of code does compiler generate
// to match a call to Object::delete if constructor
// throws anywhere??? is it inline???
// does it have a try...catch???
SpecialGraph* graph = new (heap) SpecialGraph(heap);

// NOTE: if the 2nd NewNode throws, there is a temporary
// leak relative to heap that is cleaned up only when
// "heap" is destroyed on scope exit
}

If I am concerned about the heap-relative leak in Graph
constructor, then I can offer a Construct() member
in graph, called just after the "new" call, to create
entry/exit nodes. However, memory errors are "fatal"
and always result in component shutdown, which means
the heap used will be destroyed, therefore no real leak.

My main concern is what kind of code is generated
by new (heap) SpecialGraph(heap). In particular, it would
seem that the compiler has to always go to the trouble of
a hidden try...catch block that invokes the placement delete
operator of Object if the constructor throws. True???

Andy


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Alberto Ganesh Barbati
Guest





PostPosted: Sun Sep 24, 2006 7:51 pm    Post subject: Re: problems with constructor throwing exception Reply with quote

andrew_nuss (AT) yahoo (DOT) com ha scritto:
Quote:
My main concern is what kind of code is generated
by new (heap) SpecialGraph(heap). In particular, it would
seem that the compiler has to always go to the trouble of
a hidden try...catch block that invokes the placement delete
operator of Object if the constructor throws. True???

True. Whether the try-block is inline or not it's implementation
defined. It could (or could not) be some surrogate try-block optimized
for this special case. The compiler may (or may not) inspect your code,
find that it doesn't throw and optimize the block away. Who knows? The
compiler can do whatever it wants as long as the "observed behaviour" is
the correct one. Does this mean that there is some hidden overhead? It's
possible. It has to say that some compilers implements try-blocks in a
way that makes the "try" have zero performance overhead (so there is
*no* performance hit if no exception is thrown). If you want the big
picture, just have a read of the Performance TR:

http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

Regards,

Ganesh

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Alberto Ganesh Barbati
Guest





PostPosted: Sun Sep 24, 2006 7:52 pm    Post subject: Re: problems with constructor throwing exception Reply with quote

andrew_nuss (AT) yahoo (DOT) com ha scritto:
Quote:

class Graph : public Object {

Heap heap;
NodePool pool;
Node* const entry;
Node* const exit;

public:

Graph (Heap& myheap) :
heap(myheap),
pool(myheap), // can't throw
entry(pool.NewNode()), // can throw
exit(pool.NewNode()) // can throw
{
}

// will never be invoked if 2nd NewNode above
// throws, implying that 1st NewNode causes
// pool growth, but without destructor in Pool
// (by design), the pool leaks.
~Graph ()
{
// since pool has no destructor
// this is the only way it is released
pool.Destroy();
}
};

Your analysis is correct. You will have a leak if the second call to
pool.NewNode() throws. You must replace Destroy() with a destructor or
call Destroy() in the destructor of NodePool. If you don't stick with
strict RAII design (costructor -> acquires, destructor -> releases) then
you *will have* problems with exceptions. If you don't follow this
advice then it's much better to forget EH and disable it completely!

Ganesh

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.