views:

325

answers:

5

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?

+4  A: 

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"

Binary Worrier
Currenly I do not wish to modify the Sample class. I am aware abt std::terminate() etc.. Having a throw() spec. for dtor is also useless as it will not prevent any exceptions from escaping nor prevent anyone from throwing. My question is more inclined towards 'What happens to memory?' Is it released? Is it leaked? Or is it Undefined behaviour? Quoting c++ standard sections will help.
Abhay
O.K. C++ standard is quite terse for me too. The only reason i am asking is; if i am to ban throwing/escaping exception from dtors in my current project, i would need some 'spec lawyer's' help :-)
Abhay
+1  A: 

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
}
stefanB
1) Fine. Well Known. But i do not intend to modify class Sample.2) Can you kindly quote c++ standard sections which deals with array deletion when an exception is thrown by dtor of one of its element. Thanks
Abhay
+1  A: 

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..

lx
"What happens if the 4th or 3rd destructor throws another exception?" terminate() is called (15.5.1).
Steve Jessop
+5  A: 

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).

anon
+2  A: 

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?

Steve Jessop
Thanks or your inputs. The Sample class is a wrapper for a thrid-party library class, that is freezed at the moment. So we wrapped it up again wherein our dtor ensures that exceptions do not escape. But the crux of the problem was; some dtors of the array were not called due to exception from a dtor of earlier elements. Infact i discovered from the C++ Std. that array dtors are called last-to-first (decreasing memory) elementwise. But the object who owns this array was deallocated despite exception from one of its array-members. This lead to serious memory corruption and random crashes.
Abhay