Topic: new Thing() failure in ctor


Author: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Fri, 28 Jan 1994 20:11:39 GMT
Raw View
Kevin J Hopps (hopps@yellow.mmm.com) wrote:
: I am issuing another plea to the committee to address what seems to me
: to be a major hole in the standard as it has evolved thus far.  If this
: has been addressed already, please forgive my plea, and update my on the
: decision.

: To reiterate:  consider the following statement.
:     Thing* tp = new Thing;
: Suppose that the memory for Thing is allocated but the constructor for
: Thing throws an exception.  How is the memory for Thing reclaimed?

: Please give us ammunition for battle with our compiler providers.
: My current implementation has a memory leak, which gets serious if
: sizeof(Thing) is large.

I have asked the same question in the thread "Exceptions and dynamic allocation.
The postings so far have not yield any good answers related to your (my) point,
but I received a mail message from Sun Microsystems which indicated that the
problem is an open issue and will likely be discussed at the March meeting.
But don't get your hopes up, the message indicated that the problem is not
easily solvable.  Here is the relevant parts of the message:

----->

The problem is that there is no way for *either* the implementation or the
user to reliably free the memory.

Here briefly is why :

 allocation and initialization are separate operations

 at the point of initialization (i.e. in the constructor) there is no
 record of how the memory has been allocated (e.g. by a user-defined
 new, by a placement new, by a class specific new), so how can
 the compiler "free" the memory after an exception in the constructor ?

 Even if there were such a record there may not *be* any "inverse"
 allocation operation e.g. a class with an operator new, but no operator
 delete (you know you'll never deallaocate any of them).

So the compiler can't do it - but neither can the user:

 X::X() {
  ...
  throw 911; // destructors for members and bases (at least)
    // executed when stack is unwound
 }

 X* p;
 try {
  p = new X;
 }
 catch( ... ) { delete p; } // will run dtor's a second time
<-----

I'm still hoping to get a better understanding of exceptions with respect to
constructors.  Based on documents and the postings I've seen, I am currently
more confused then when I first began inquiring.  It seems according to the
postings (and the above mail message) that the compiler will run destructors
on the sub-objects of the object being constructed (along with any local
objects).  This is not indicated in any of the literature I have seen on
exception handling.  If this is the case then the object itself is 9/10ths
destructed (The last 1/10th should be provided by the compiler somehow).
If this is not the case then all constructor code should catch any exception,
make the object a fully constructed "zombie", and rethrow the exception, and
the compiler should force the assignment of the objects memory to the pointer
somehow (so it can be destructed in the catch).  I believe the former is the
better of the two possiblities, but as the mail message indicates this is not
easily implemented.

I think we should merge this discussion into one thread.  Any suggestion on
how we can accomplish this?

--Stephen Cipolli





Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 30 Jan 1994 07:13:13 -0500
Raw View
Stephen Cipolli quoted:

>The problem is that there is no way for *either* the implementation or the
>user to reliably free the memory.
>
>Here briefly is why :
>
>        allocation and initialization are separate operations
>
>        at the point of initialization (i.e. in the constructor) there is no
>        record of how the memory has been allocated (e.g. by a user-defined
>        new, by a placement new, by a class specific new), so how can
>        the compiler "free" the memory after an exception in the
>constructor ?
>
>        Even if there were such a record there may not *be* any "inverse"
>        allocation operation e.g. a class with an operator new, but no
operator
>        delete (you know you'll never deallaocate any of them).

> So the compiler can't do it - but neither can the user:

The compiler can do it. The compiler must generate code to call operator
new (whatever the source) and then the constructor(s). Between these calls
the compiler can add the object pointer to a linked list of allocated objects
under construction; when the constructor returns, the object is removed
from the list. If an exception is thrown out of a function, all objects on
the under construction list for the function can be deleted.

Locating the appropriate operator delete is more interesting, but it's
analogous to managing a vtable, only for a single object. If a class has no
operator delete, the system operator is used. The behavior if a class hides
operator delete is, like most of this, undefined at the moment, but the
state is discoverable, which is all that matters.

If you don't find this constructive (pun intended) proof convincing, you
can always examine the compilers that (I'm told) already implement it.

Bob Foster
objfactory@aol.com




Author: ball@cygany.Eng.Sun.COM (Mike Ball)
Date: 31 Jan 1994 23:59:43 GMT
Raw View
In article 7it@news.delphi.com, bobkf@news.delphi.com (BOBKF@DELPHI.COM) writes:
> Locating the appropriate operator delete is more interesting, but it's
> analogous to managing a vtable, only for a single object. If a class has no
> operator delete, the system operator is used. The behavior if a class hides
> operator delete is, like most of this, undefined at the moment, but the
> state is discoverable, which is all that matters.
>
> If you don't find this constructive (pun intended) proof convincing, you
> can always examine the compilers that (I'm told) already implement it.

I have yet to discover one of these implementations that I can't screw up
with some "reasonable" form of allocation and deletion.

Unfortunately, there are cases where only the mind of the programmer knows
what to do.

I also have problems with your suggested implementation in that it adds considerable
costs for every operator new, whether exceptions during the constructor are
possible or not.

This IS an important question.  We can't always get the answer we like, and we
may have to accept something less than beautiful.  I don't consider the current
answers provide adequate guidance for an implementation.  For one thing, the
current answers are all at the implementation level rather than at the language
level.  What happens to storage in what situation is beyond the scope of the
standard, which can only state what routines are called.

Please don't pretend that this is solved, and it's only the stubborn compiler
vendors who are dragging their feet.  Keep working on it and come up with a
real, language-level spec which can be part of the standard.

-Mike Ball-






Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 1 Feb 1994 07:30:10 GMT
Raw View
ark@tempel.research.att.com (Andrew Koenig) writes:

>In article <hopps.759599854@mmm.com> kjhopps@mmm.com writes:
>
>> To reiterate:  consider the following statement.
>>     Thing* tp = new Thing;
>> Suppose that the memory for Thing is allocated but the constructor for
>> Thing throws an exception.  How is the memory for Thing reclaimed?
>
>The memory should be deallocated before passing the exception on
>to the calling context.

I think you missed the point.  Who should deallocate the memory,
the compiler or the programmer?  If the compiler does it,
what function will it call to deallocate the memory?
If the programmer must do it, then how?

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Tue, 1 Feb 1994 07:54:57 GMT
Raw View
sfc@datascope.com (Steve F. Cipolli (P/M)) writes:

>The postings so far have not yield any good answers related to your (my) point,
>but I received a mail message from Sun Microsystems which indicated that the
>problem is an open issue and will likely be discussed at the March meeting.
>But don't get your hopes up, the message indicated that the problem is not
>easily solvable.  Here is the relevant parts of the message:

I got the same message. Here's my response to it:

| The problem is that there is no way for *either* the implementation or
| the user to reliably free the memory.
|
| Here briefly is why :
|
| allocation and initialization are separate operations

Actually there are three pieces of code involved: the allocation
(done by operator new()), the initialization (done by the constructor),
and the piece of code which calls the constructor and operator new().
[Although in some implementations this piece of code may just call
the constructor, which calls operator new() itself].

| at the point of initialization (i.e. in the constructor) there is no record of
| how the memory has been allocated (e.g. by a user-defined new,
| by a placement new, by a class specific new), so how can
| the compiler "free" the memory after an exception in the constructor ?

It can be done by catching the exception in the code that calls
operator new(), deallocating the memory, and then rethrowing.

> Even if there were such a record there may not *be* any "inverse"
> allocation operation e.g. a class with an operator new, but no operator
> delete (you know you'll never deallaocate any of them).

In this case, there is no real problem - the compiler could just
not worry about deallocating the memory.

The real problem is with overloaded ("placement") operator new():
currently you _can't_ overload the corresponding operator delete().  I
think the only way that this can be solved is to allow users to
overload operator delete().

> So the compiler can't do it - but neither can the user:
>
>  X* p;
>  try {
>   p = new X;
>  }
>  catch( ... ) { delete p; } // will run dtor's a second time

No, even worse: will call delete on an uninitialized pointer.
The assignment to p never gets executed.

--
Fergus Henderson - fjh@munta.cs.mu.OZ.AU




Author: bobkf@news.delphi.com (BOBKF@DELPHI.COM)
Date: 27 Jan 1994 03:05:29 -0500
Raw View
hopps@yellow.mmm.com (Kevin J Hopps) writes:

>I am issuing another plea to the committee to address what seems to me
>to be a major hole in the standard as it has evolved thus far.  If this
>has been addressed already, please forgive my plea, and update my on the
>decision.

>To reiterate:  consider the following statement.
>    Thing* tp = new Thing;
>Suppose that the memory for Thing is allocated but the constructor for
>Thing throws an exception.  How is the memory for Thing reclaimed?

>Please give us ammunition for battle with our compiler providers.
>My current implementation has a memory leak, which gets serious if
>sizeof(Thing) is large.

I would like to add my voice to the plea. The ARM and/or September draft
standard do not require that memory allocated by new is freed when a
constructor fails. It seems to be left up to the good intentions of the
compiler writer.

It can't be that only people in Minnesota see this as a problem.

Bob Foster
objfactory@aol.com




Author: ark@tempel.research.att.com (Andrew Koenig)
Date: 26 Jan 94 22:31:01 GMT
Raw View
In article <hopps.759599854@mmm.com> kjhopps@mmm.com writes:

> To reiterate:  consider the following statement.
>     Thing* tp = new Thing;
> Suppose that the memory for Thing is allocated but the constructor for
> Thing throws an exception.  How is the memory for Thing reclaimed?

The memory should be deallocated before passing the exception on
to the calling context.
--
    --Andrew Koenig
      ark@research.att.com




Author: hopps@yellow.mmm.com (Kevin J Hopps)
Date: Wed, 26 Jan 94 16:09:17 GMT
Raw View
I am issuing another plea to the committee to address what seems to me
to be a major hole in the standard as it has evolved thus far.  If this
has been addressed already, please forgive my plea, and update my on the
decision.

To reiterate:  consider the following statement.
    Thing* tp = new Thing;
Suppose that the memory for Thing is allocated but the constructor for
Thing throws an exception.  How is the memory for Thing reclaimed?

Please give us ammunition for battle with our compiler providers.
My current implementation has a memory leak, which gets serious if
sizeof(Thing) is large.
--
Kevin J. Hopps   e-mail: kjhopps@mmm.com
3M Company   phone: (612) 737-3300
3M Center, Bldg. 235-3B-16 fax: (612) 737-2700
St. Paul, MN 55144-1000