Topic: An exception handling question


Author: robert@brt.com (Robert Blumen)
Date: Thu, 7 Oct 1993 20:48:52 GMT
Raw View
Fergus James HENDERSON (fjh@munta.cs.mu.OZ.AU) wrote:
: Jim Lloyd <jlloyd@symantec.com> writes:
:
: >I have a question concerning a subtle aspect of C++ exception
: >handling that I have not seen addressed elsewhere.  When an
: >object is created in the heap with new, there are two steps:
: >1) operator new to allocate the raw memory, and 2) constructor
: >execution.  If the first step completes succesfully, but the
: >second step fails with an exception, is the compiler
: >responsible for ensuring that an operator delete is called to
: >undo the first step?
:
: This question came up on comp.std.c++ a while ago, and I think we
: concluded that although there was nothing in the ARM that conclusively
: indicating that the compiler was responsible for calling operator
: delete in this situation, common sense suggested that this was an
: accidental omission, because otherwise there is as you point out no
: (easy) way to prevent memory leaks.  Hopefully the standards committee

and later:

Fergus James HENDERSON (fjh@munta.cs.mu.OZ.AU) wrote:
: maxtal@physics.su.OZ.AU (John Max Skaller) writes:
:
: >Smart pointers will only work if the compiler keeps track of
: >every individual member being initialised (so it can
: >destroy each one that is initialised an no more).
:
: The compiler is already definitely required to do this for automatic
: objects (ARM 15.3), and I don't think it would be any harder for heap
: objects.

In the case of automatic objects, the compiler will call the destructor
for the constructed object on the heap.  If the question concerns
allocating an  object on the heap using a pointer which is simply
a local automatic variable, then this memory is lost.  This would be
the case even if the second step completed without an exception.  The
exception handling mechanism is only respnosible for "calling destructors
for automatic objects constructed on the path from a try-block to a throw-
expression [ARM, 15.3c".

In the case where the pointer was a member of an object, I am not sure
that it would be correct for the compiler to delete it.  I suspect
that one could construct situations in the presence of overloaded
operator new and delete for that class being new'ed in which deleting
the object was not the right thing to do.   This seems to me to be
the question of what should exception handling do with partially
constructed objects.  The ARM says that "only the fully constructed sub-
objects will be destructed".  This does seem to me to leave open the
possibility that the memory chunk would be leaked.  But is there
an alternative?

Robert
--
------------------------------------------------------------------------------
Robert Blumen    | Berkeley Research and Trading
Software Engineer   | 1600 Shattuck Ave
robert@brt.com    | Suite 222




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 2 Oct 1993 22:12:28 GMT
Raw View
In article <28fr3pINN53i@grumpy.symantec.com> Jim Lloyd <jlloyd@symantec.com> writes:
>I have a question concerning a subtle aspect of C++ exception
>handling that I have not seen addressed elsewhere.  When an
>object is created in the heap with new, there are two steps:
>1) operator new to allocate the raw memory, and 2) constructor
>execution.  If the first step completes succesfully, but the
>second step fails with an exception, is the compiler
>responsible for ensuring that an operator delete is called to
>undo the first step?
>

 Yes. Its got to be responsible.  Unfortunately,
the mem-initialiser syntax doesnt work well with exceptions.
In fact, sticking pointers to dynamically allocated memory anywhere
doesnt work well either. References in a class are even
worse.

 struct X {
  A const* a;
  B const* b;
  X() :a(new A), b(new B) {}
 };

 struct Y {
  A & a;
  B & b;
  Y() :a(*new A), b(*new B) {}
 };

Neither of these classes can be initialised so 'a' and 'b'
refer to objects created by the constructor without
danger of a memory leak.

If a is allocated and b throws an exception, a is leaked. Too bad.
Use smart or non-const pointers.

Non-const pointers work by doing the initialisation in the body
of the constructor where you can wrap a try block around each
allocation.

Smart pointers will only work if the compiler keeps track of
every individual member being initialised (so it can
destroy each one that is initialised an no more).

[Andrew Koenig et al have shown this can be done
with no execution time overhead by tracking where the
program counter will be with a table, although I think
that it will be trickier initialising an array?]

[Andrew also devised a technique that allows pointers to
be stored on the stack and the object they point to cleaned
up if an exception is thrown, see a recent JOOP article]

[I'm fascinated by the credence given to arguments about
breaking existing code, appied to various topics and
language extensions, when exception handling, and especially
the throwing of xalloc on memory allocation failures
will radically change the way we use C++ anyhow]

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Mon, 4 Oct 1993 15:35:42 GMT
Raw View
Jim Lloyd <jlloyd@symantec.com> writes:

>I have a question concerning a subtle aspect of C++ exception
>handling that I have not seen addressed elsewhere.  When an
>object is created in the heap with new, there are two steps:
>1) operator new to allocate the raw memory, and 2) constructor
>execution.  If the first step completes succesfully, but the
>second step fails with an exception, is the compiler
>responsible for ensuring that an operator delete is called to
>undo the first step?
[...]
>When I try this code with one compiler that supports exceptions
>(Watcom), the exception is caught, but operator delete is not
>called.  I find nothing in the ARM to indicate whether this is
>correct behavior or not.  If it is correct behavior, how is
>one to write code to prevent the memory leak?

This question came up on comp.std.c++ a while ago, and I think we
concluded that although there was nothing in the ARM that conclusively
indicating that the compiler was responsible for calling operator
delete in this situation, common sense suggested that this was an
accidental omission, because otherwise there is as you point out no
(easy) way to prevent memory leaks.  Hopefully the standards committee
will remedy this.  If I were you I would file a bug report with Watcom,
explaining that their behaviour was wrong because it violates the
"spirit" of ARM 15.3 and makes preventing memory leaks very difficult.

(The issue is as you say a subtle one and compiler implementors and
even authors of language reference manuals can be forgiven for failing
to consider it.)

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




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Mon, 4 Oct 1993 17:36:10 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>Smart pointers will only work if the compiler keeps track of
>every individual member being initialised (so it can
>destroy each one that is initialised an no more).

The compiler is already definitely required to do this for automatic
objects (ARM 15.3), and I don't think it would be any harder for heap
objects.

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




Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 4 Oct 1993 21:29:05 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

[regarding exception-handling-style cleanup of allocated memory to
prevent leaks.]

>Andrew Koenig et al have shown this can be done
>with no execution time overhead by tracking where the
>program counter will be with a table, although I think
>that it will be trickier initialising an array?

Emphasis on the "et al".  This trick has been around a long time.  I
learned it from people at Acorn who used it for Modula-2+, and I
understand that they learned it from CLU.  It's been used in other
places as well.

As to freeing allocated memory that is assigned to array elements, the
engineering-cheap answer to that problem is to (in one PC-range) clear
the array, and (in the next) allocate the memory to the elements
sequentially.  The cost of zeroing the array will not be too large
compared to the cost of allocating memory and assigning the addresses.
(You need not standardize on this, of course -- version 0 of such a
system uses the slightly-slow-but-simple-and-sure trick outlined here,
and version 1 (or 2 or 3) might generate code that deallocates only
what has already been allocated.)

David Chase
Sun (speaking for myself)




Author: Jim Lloyd <jlloyd@symantec.com>
Date: 6 Oct 1993 03:29:16 GMT
Raw View
In article <9327803.4475@mulga.cs.mu.OZ.AU> Fergus James HENDERSON,
fjh@munta.cs.mu.OZ.AU writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>Smart pointers will only work if the compiler keeps track of
>>every individual member being initialised (so it can
>>destroy each one that is initialised an no more).
>
>The compiler is already definitely required to do this for automatic
>objects (ARM 15.3), and I don't think it would be any harder for heap
>objects.
>
>--
>Fergus Henderson                     fjh@munta.cs.mu.OZ.AU

If I am correct in my assumption that the above message is part
of a chain of responses from my original article "An exception
handling question", then I must conclude that my newsfeed has
missed all intermediate messages in this thread, since this is
the first response in comp.std.C++ that I have seen. At the
risk of having my e-mail inbox flooded, may I request that
some kind soul forward to me a transcript of this thread?
Please e-mail to jlloyd@symantec.com.

Thanks in advance,

Jim Lloyd
jlloyd@symantec.com




Author: Jim Lloyd <jlloyd@symantec.com>
Date: 30 Sep 1993 23:46:01 GMT
Raw View
I have a question concerning a subtle aspect of C++ exception
handling that I have not seen addressed elsewhere.  When an
object is created in the heap with new, there are two steps:
1) operator new to allocate the raw memory, and 2) constructor
execution.  If the first step completes succesfully, but the
second step fails with an exception, is the compiler
responsible for ensuring that an operator delete is called to
undo the first step?

Consider this code sample:

----- cut here -----
#include <stdio.h>
#include <stdlib.h>

class CTester
{
public:
    CTester();
    virtual ~CTester();
};

CTester::CTester()
{
    throw "Here is an exception";
}

CTester::~CTester()
{
}

void* operator new(size_t size)
{
    return ::malloc(size);
}

void operator delete(void* p)
{
    puts("In ::operator delete");
    ::free(p);
}

int main()
{
    try
    {
        CTester* w = new CTester();
    }
    catch(char* msg)
    {
        puts(">main: caught an exception");
    }

    return 0;
}
----- cut here -----

When I try this code with one compiler that supports exceptions
(Watcom), the exception is caught, but operator delete is not
called.  I find nothing in the ARM to indicate whether this is
correct behavior or not.  If it is correct behavior, how is
one to write code to prevent the memory leak?

Thanks in advance,

Jim Lloyd
jlloyd@symantec.com