Question

We have a class whose semantic behaviour is like the following :-

struct Sample
{
  ~Sample() throw() 
  {
    throw 0;
  }
};

void f ()
{
  try
  {
    delete new Sample;
  }
  catch (...){
  }
}

I know that throwing exceptions in dtors is evil; but the relinquishment of a 3rd Party library resource is throwing an exception (but can be re-accquired immediately, something strange!). There is also a pool of this resource, say an array/container of class Sample. So, there are two cases to consider: destruction of a dynamically allocated object and destruction of a dynamically allocated array of objects.

Currently the application crashes randomly at different execution-points only when the array version (pool) is used. We believe this is due to memory corruption but then why does the unpooled version work?.

What happens to the allocated memory? Is it undefined behaviour? What happens in the case of an array? Do the dtors (atleast, not memory) of all the elements of an array (say if the dtor of the first element throws) get called?

Thanks in advance,

EDIT-1: Well, we tracked it down to dtors of some array-elements not being called. But the allocated memory does not seem to have problems... Following is section 5.3.5.7 of SC22-N-4411.pdf)

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be
called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some
element of the array throws an exception. —end note ]

<\snip>

Looks like memory is always deallocated in such cases. Am i right in interpreting the standard?

Was it helpful?

Solution

There are two things that could happen in this situation:

  • terminate() is called
  • undefined behaviour

In neither case can dynamically allocated memory be guaranteed to be released (except that application termination will of course return all resources to the OS).

OTHER TIPS

C++ will terminate your application if a dtor throws an exception while the stack is being unwound because of another exception.

As it's practically impossible to determine under what circumstances a dtor is called, the standard rule is to never throw exceptions from dtors.

If your 3rd Party Library is throwing an exception, catch it in your dtor, log it, or save it's state to some static cache where you can pick it up "later", but don't allow it to escape out of your dtor.

Do this, then see if your collection of objects work, it could be causing your crashes.

UPDATE

Unfortunately I'm not a spec lawyer, preferring the Fisherman's Friend approach of "suck it an see".

I'd write a small app with a class that allocates a meg off the heap. In a loop, make an array of the classes, have the classes dtor throw an exception, and throw a catch an exception at the end of the loop (causing the stack to unwind and call the dtors of the array of classes) and watch it to see your VM usage go through the roof (which I'm pretty sure it will).

Sorry I can't give you chapter and verse, but that's my "belief"

Since you asked in a comment for chapter and verse:

15.2:3 has a note, saying:

"If a destructor called during stack unwinding exits with an exception terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor"

As far as I can make out, the only justification for saying "generally" there, is that it's possible to very carefully write a program so that no object whose destructor can throw, is ever deleted as part of stack unwinding. But that's a harder condition to enforce in the average project, than "destructors must not throw".

15.5.1 and 2 say:

"In the following situations ... -- when the destruction of an object during stack unwinding (15.2) exits using an exception ... void terminate() is called".

There are some other conditions for terminate() in 15.5.1, which suggest other things which you might want not to throw: copy constructors of exceptions, atexit handlers, and unexpected. But for example the most likely reason for a copy constructor to fail is out of memory, which on e.g. linux might segfault instead of throwing an exception anyway. In such situations terminate() doesn't seem so bad.

Looks like memory is always deallocated in such cases. Am i right in interpreting the standard?

Looks to me as though the memory for the object being deleted is always deallocated. It doesn't follow that any memory which it owns via pointers, and frees in its destructor, is deallocated, especially if it's an array and hence there are several destructors to call.

Oh yes, and do you trust your third-party library to be exception-safe? Is it possible that the exception during free is leaving the library in a state which its authors didn't anticipate, and that the crash is because of that?

1) Throwing an exception from destructor is bad because if exception is being processed and another exception occurs the application will exit. So if during exception handling your application clears objects (e.g. calls destructor on each of them) and one of the destructors throws another exception the application will exit.

2) I don't think that destructors get called automatically for the rest of elements in container when one of them throws exception. If the exception is thrown in container's destructor then the rest of elements will definitely not be cleaned up as the application will unwind the stack while handling the exception.

The standard way of writing destructor should be something like:

A::~A()
{
   try {
         // some cleanup code
   }
   catch (...) {} // Too bad we will never know something went wrong but application will not crash
}

Destructors must never throw exceptions, it leads to undefined behaviour and could also lead to memory leaks. Let's consider the following example

T* p = new T[10];
delete[] p;

So, how will new[] and delete[] react if T throws a destructor?

Let's first consider that the constructions all went smoothly and then during the delete[] the fourth or so destructor throws. delete[] can choose to propagate the exception which would lead to all the other T objects which are left in the array being lost, not retrievable and therefore undestroyable. It also can't "catch" the exception because then delete wouldn't be exception-neutral anymore.

Second, say one of the constructors throws. Say the 6th constructor throws an exception. During stack unwinding all objects which have been constructed till now have to be deconstructed. So the 5th, 4th, 3rd and so on destructor get's called. What happens if the 4th or 3rd destructor throws another exception? Should it be absorded or propagated?

There's no answer to this, so this topic leads to undefined behaviour.

And as outlined in my first example, could also lead to memory leaks..

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top