Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-11-04 13:39:05


"E. Gladyshev" <egladysh_at_[hidden]> writes:

> --- David Abrahams <dave_at_[hidden]> wrote:
>
> [...]
>> > It catches all exceptions, so there is nothing safe
>> > after that. I might be missing something but
>> > I think that it is creates a very bad impression
>> > that after you put catch(...), it is safe
>> > to use the object.
>>
>> Show me any C++ code you think is safe, and I will show you that your
>> claim of safety relies on all the same conditions.
>
> I think that the following code is much safer.

Why?

Rather than wait for your answer, because I really need to quit this
thread and spend my attention elsewhere, I'll anticipate it.

  I think you mean that exceptions whose types you don't know, and
  which indicate some fundamentally broken state of the system, might
  be thrown. I think you're worried that catch(...) will stop these
  exceptions and your program will continue ahead as though nothing
  had gone wrong.

If I've anticipated wrong, I'm sorry, but you'll have to get your
questions answered elsewhere because I'm out of time. If not, recall
that the paper was written about the process of standardizing C++ and
not about specific operating systems and their degenerate
fault-handling schemes (**). Here are the problems with your
assertion, from the point-of-view of standard C++:

  1. There's no reason to think that a fundamentally broken state of
     the system leads to a C++ exception, any more than it leads
     directly to launching a missile or reformatting your hard drive.

  2. In your code below, the same reasoning means that if p->f()
     causes undefined behavior, you have no right to expect that
     execution will reach the catch(...) clause; the program is just
     as likely to end up executing "delete p". Undefined behavior
     means just that: the behavior is arbitrary.

> The first precondition is that you have to know what exceptions a
> "correct" implementation of a class can possibly generate. Let's
> assume that that the correct method, my_type::f() can generate only
> one exception, out_of_memory and my_type provides basic guarantees.

  2a. (corrolary) Undefined behavior may end up throwing one of those
      exceptions which you think only a "correct" implementation of
      your class can generate.

<snip>

(**) if you want to read about those issues, see the "what about
     programmer errors?" section of
     http://www.boost.org/more/error_handling.html. Believe me, they
     have been considered.

> Then the safer code is
>
> my_type *p = new my_type
> try
> {
> p->f();
> }
> catch( out_of_memory ) //it is safe to use 'p'
> {
> delete p;
> }
> catch( ... ) //nothing is safe, don't use 'p'
> {
> exit(); //or throw; or while(1) {} just try to stop the execution here by any means
> }
>
>
>>
>> > To fix the above code you need list all
>> > known exception types before catch(...).
>>
>> Why do you think so?
>
> See the above, when you do catch(...), you are cathing
> all exception including the one that can be caused
> by incorrect program.

In an incorrect program, anything can happen including direct
launching of a missile without throwing an exception.

>> > In catch(...), you just better do nothing.
>>
>> Nothing *is* done "in catch(...)"
>
> I think you know what I meant.

I did not.

> You cannot use the object after catch(...) as in your example.

In standard C++, yes, you can.

>> but instead let me suggest that you make a good, careful study of
>> the issues and consider the full context of my article before you
>> decide you know how to handle exceptions more safely than the rest
>> of us.
>
> What do you mean by "rest of us"?

Those of us who have not only yesterday begun to come to grips with
the principles of using invariants in stateful programs. I don't
mean to be pretentious, but it's a bit early for you to start
"correcting the experts".

> Thanks to this thread, I think that I can define
> safer exception handling techniques for myself.
>
> 1. If your method breaks an invariant internally and calls
> an outside method, make sure that you know all exceptions that
> a correct implementation of the outside method can generate.
> Catch the know exceptions *explicitly* and restore invariants.

Using the destructor of an automatic object to restore invariants is
probably a better in most cases.

> See the next item on what to do in catch(...)
>
> 2. If you call a method f() that provides basic guarantees,
> make sure to *explicitly* catch all exceptions that correct
> f() can possibly generate. It is possibly safe to continue
> the execution after a know exception.
> In the catch(...) block, assume that nothing is safe,
> use h/w interlocks or other system means to stop
> the execution immediately.
>
> This is what I would do to keep the missle in place. :)

A better solution, if your OS is prone to throwing exceptions from
hardware faults or other "undefined behavior" conditions, is to
instruct the OS to abort() immediately by inserting handlers at the
lowest level, before any unwinding can occur. In order to enter your
catch(...) block the compiler must unwind the stack, executing
destructors, which hardly constitutes an "immediate exit". If you
can't do that, you're usually much better off allowing those
exceptions to leak out of the program completely, since in that
situation many compilers will do no stack unwinding at all.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk