Topic: Memory leaks with exception thrown from a new'ed constructor


Author: cbarber@bbn.com (Christopher Barber)
Date: 22 Apr 1994 16:12:21 GMT
Raw View
>>>>> "maxtal" == John Max Skaller <maxtal@physics.su.OZ.AU> writes:

    maxtal> In article <9408721.21250@mulga.cs.mu.OZ.AU>
    maxtal> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
    >> What about the following situation:
    >>
    >> class Bar bar; Foo *foo = new(bar) Foo;
    >>
    >> Exactly how will the system delete it for me now?

    maxtal>  Yes, thats nasty. There's been talk of placement delete
    maxtal> operators.

    maxtal>  I suspect there is no resolution. If you also coded

    maxtal>  void * operator new(size_t n, X) { return malloc(n); }
    maxtal>     void * operator new(size_t, Y y) { return &y; }

    maxtal> you have to code a operator delete that handles both cases.

Not necessarily.  One approach would be to extend the syntax for the
declaration of operator new to specify a function to free the memory
if the constructor throws an exception:

   void free_on_exception(void *ptr) { free(ptr); };

   void * operator new(size_t n, X) free_on_exception { return malloc(n);};
   void * operator new(size_t, Y y) { return &y; };

   Foo *foo1 = new (anX) Foo() ; // Memory for Foo freed on exception
   Foo *foo2 = new (aY) Foo(); // Nothing done when Foo() hurls

Under this syntax, the declaration for the default operator new might
look like:

   void *operator new (size_t) ::operator delete ;

I am not wedded to this particular syntax but think that we need to
do something like this in order to avoid having to require nasty
hacks when using placement new.

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 9 Apr 1994 21:17:13 GMT
Raw View
In article <9408721.21250@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>>
>>>When Foo() throws the exception, there is now way to delete the storage
>>>that was allocated before the constructor was called.
>>
>> Thats a good thing, because the system will delete
>>it for you.
>
>Please elaborate.  Has the committee really addressed this issue yet?

 Yes and No. At San Diego, it was agreed that the
operator delete corresponding to the operator new would be called;
that is, the delete that would be called had it bee written
explicitly:

 X* x = new X; // if the constructor throws up then
 delete x;     // the same delete as this one is invoked
        // to reclaim storage

 The significant change here (I believe) is that
the rules for member operator new is now fixed: the corresponding
delete is called here too, not just for global operator new.

 The issue of placement forms of new and delete is
a current hot topic on the committee reflectors.

>What about the following situation:
>
> class Bar bar;
> Foo *foo = new(bar) Foo;
>
>Exactly how will the system delete it for me now?

 Yes, thats nasty. There's been talk of
placement delete operators.

 I suspect there is no resolution. If you also coded

 void * operator new(size_t n, X) { return malloc(n); }
 void * operator new(size_t, Y y) { return &y; }

you have to code a operator delete that handles both cases.
To do that, you have to put some flag in the store.

 void * operator new(size_t n, X) {
  void *t = malloc(n+1);
  *(char*)t='1';
  return (char*)t + 1;
 }
 void * operator new(size_t, Y y) {
  *(char*)(void*)&y='2';
  return (char*)(void*)y + 1;
 }
 void operator delete(void *p)
 {
  switch( *(char*)p )
  {
   case '1': free(p); break;
   case '2': break;
  }
 }

Given that, the compiler OUGHT to call operator delete, which
has to figure out what to do.

--
        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 Henderson)
Date: Sat, 26 Mar 1994 15:53:54 GMT
Raw View
cbarber@bbn.com (Christopher Barber) writes:

>I am sure that someone has already brought this up at some point,
>but just in case....
>
>Is the standards comittee addressing the following hole in the language?
>
>   Foo *foo = new Foo() ;
>
>When Foo() throws the exception, there is now way to delete the storage
>that was allocated before the constructor was called.

Yes, this has come up here a couple of times before.
Last time it came up, someone from the standards committee said
it was on their list of items for consideration and might be
addressed at the next committee meeting.

>It seems to me that all that is need to fix this in the language is
>to require the compiler to arrange to delete memory allocated by a new
>operator when it encounters an exception thrown by a called constructor.

Yes.

>It would also be necessary to either make delete smart enough to handle
>non-heap addresses safely or to add some extra syntax to the definition
>of the new operator to specify whether delete should called.

This would be useful, but it's not enough, IMHO.
It would be almost impossible to use you own storage allocator
by overloading operator new(), since you still wouldn't be able to avoid
the memory leak in this case.

A better approach would be to somehow specify not just whether delete
should be called, but also specify _which_ delete.  One way to do
this would be to allow overloading of operator delete using a
similar syntax to overloaded operator new, and specify that the
compiler should call the *corresponding* operator delete(), if there
is one, with the same arguments that were passed to operator new().

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 28 Mar 1994 11:24:12 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>cbarber@bbn.com (Christopher Barber) writes:
>>I am sure that someone has already brought this up at some point,
>>but just in case....
>>
>>Is the standards comittee addressing the following hole in the language?
>>
>>   class Foo {
>>       Foo() ;
>>       // ...
>>   };
>>
>>   Foo::Foo()
>>   {
>>      // ...
>>      if (something_bad_happens)
>>          throw X();
>>   }
>>
>>   Foo *foo = new Foo() ;
>>
>>When Foo() throws the exception, there is now way to delete the storage
>>that was allocated before the constructor was called.
>
> Thats a good thing, because the system will delete
>it for you.

Please elaborate.  Has the committee really addressed this issue yet?
What about the following situation:

 class Bar bar;
 Foo *foo = new(bar) Foo;

Exactly how will the system delete it for me now?

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: cbarber@bbn.com (Christopher Barber)
Date: 22 Mar 1994 14:55:48 GMT
Raw View
I am sure that someone has already brought this up at some point,
but just in case....

Is the standards comittee addressing the following hole in the language?

   class Foo {
       Foo() ;
       // ...
   };

   Foo::Foo()
   {
      // ...
      if (something_bad_happens)
          throw X();
   }

   Foo *foo = new Foo() ;

When Foo() throws the exception, there is now way to delete the storage
that was allocated before the constructor was called.

One could have Foo delete itself prior to throwing:

   Foo::Foo()
   {
      if (something_bad_happens)
      {
          delete this ;
          throw X();
      }
   }

but this is not safe in general because delete has undefined behavior
when Foo was not called from the new operator, although this problem
can be averted by overloading delete to recognize addresses which are
not on the heap.  However, there is still a problem when the exception
is thrown by something in Foo's initialization list:

   Foo::Foo()
       : bar() // may throw an exception
   {
       // Whoops, we can't catch the above exception :-(
   }

   Foo *foo = new Foo() ; // out here it is too late

A way around this would be to explicitly separate allocation from
initialization:

   char *space_for_foo = new char[sizeof(Foo)] ;
   try {
       Foo *foo = new (space_for_foo) Foo() ;
   }
   catch (...)
   {
       delete space_for_foo ;
   }

but this is rather ugly and prevents a class-specific new operator
from being invoked.

---

It seems to me that all that is need to fix this in the language is
to require the compiler to arrange to delete memory allocated by a new
operator when it encounters an exception thrown by a called constructor.

It would also be necessary to either make delete smart enough to handle
non-heap addresses safely or to add some extra syntax to the definition
of the new operator to specify whether delete should called.  An example
of the latter approach might be:

   void *operator new (size_t) delete ;
   // default new operator - deletes when a constructor throws

   void *operator new (size_t, void *addr) ;
   // placement new operator - does nothing when constructor throws

- Chris

--
Christopher Barber
(cbarber@bbn.com)




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 23 Mar 1994 16:49:30 GMT
Raw View
In article <CBARBER.94Mar22095548@apricot.bbn.com> cbarber@bbn.com (Christopher Barber) writes:
>I am sure that someone has already brought this up at some point,
>but just in case....
>
>Is the standards comittee addressing the following hole in the language?
>
>   class Foo {
>       Foo() ;
>       // ...
>   };
>
>   Foo::Foo()
>   {
>      // ...
>      if (something_bad_happens)
>          throw X();
>   }
>
>   Foo *foo = new Foo() ;
>
>When Foo() throws the exception, there is now way to delete the storage
>that was allocated before the constructor was called.

 Thats a good thing, because the system will delete
it for you.

--
        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: sfc@datascope.com (Steve F. Cipolli (P/M))
Date: Wed, 23 Mar 1994 18:28:24 GMT
Raw View
Christopher Barber (cbarber@bbn.com) wrote:
: I am sure that someone has already brought this up at some point,
: but just in case....

: Is the standards comittee addressing the following hole in the language?

:    class Foo {
:        Foo() ;
:        // ...
:    };

:    Foo::Foo()
:    {
:       // ...
:       if (something_bad_happens)
:           throw X();
:    }

:    Foo *foo = new Foo() ;


This problem was discussed in one or more threads, a month or so ago.  The
committee I believe will be looking at it soon.  The answer will be something
like what you described (the objects memory will be freed on throw from
constructor).  There are however serveral issues which need to be considered.
The discussion centered around what would happen with overloaded new and
delete and the new/placement stuff.  I don't remember much more than that.

Stephen Cipolli
Datascope Corp.
These opinions are mine alone.