Jump to page: 1 2 3
Thread overview
[phobos] Exception chaining
Jan 11, 2011
Don Clugston
Jan 11, 2011
Don Clugston
Jan 11, 2011
Jonathan M Davis
Jan 12, 2011
Jonathan M Davis
Jan 12, 2011
Don Clugston
Jan 12, 2011
Don Clugston
Jan 12, 2011
Jonathan M Davis
Jan 12, 2011
Don Clugston
Jan 12, 2011
Sean Kelly
Jan 12, 2011
Don Clugston
Jan 13, 2011
Sean Kelly
Jan 12, 2011
Max Samukha
Jan 12, 2011
Don Clugston
Jan 12, 2011
Max Samukha
Jan 12, 2011
Sean Kelly
Jan 12, 2011
Don Clugston
Jan 11, 2011
Sean Kelly
January 11, 2011
I believe I have got TDPL exception chaining working correctly using
Windows Structured Exception Handling.
(This was far from easy!)
Central to making chaining work correctly, is that chaining must only occur
when a collision occurs (not merely when two exceptions are in flight,
because one may be caught before it has any effect on the other). This
means that multiple chains of exceptions
may be in flight at any given time.
My code works in all nasty corner cases I've tested, including
multi-level collisions,
where two exceptions collide in a function, then collide again with an
even earlier exception chain in a finally block in a different function.

So the general scheme appears to work.
But, there's something I'm unclear about. When should chained
exceptions be catchable?
They are very nasty creatures, and you really want to know when they happen.
Presumably, an AssertError which occurs while processing an
FileException, should not be silently chained
and caught in the FileException.
In fact, should a chain containing an Error be catchable at all?
(If not, it still has to at least be catchable in the catchall handler
that wraps main()).
Many other schemes are possible, but I think it's important that the
rules remain simple.

One simple solution would be to make chained exceptions only catchable
by catch(Throwable).
January 11, 2011
Wow, this is incredible news!

Thanks Don for working on this. Solid exception handling is a huge selling point for D.

Regarding collateral throwables that are not Exception, good point (and I agree that the solution should be simple). TDPL doesn't discuss that issue, but it says that the initially-thrown exception is the "boss" and that everybody follows, so a possible design is to simply make the Throwable part of the chain.

I'd want to have chained exceptions still catchable by catch (Exception) because it would be a first to have the contents of the data influence its type. As far as the type system is concerned, catch (Exception) should catch Exceptions, whether or not they have a tail.

One possibility would be to move the Throwable to the front of the list. This also has its issues, for example the stack is unwound for a while and then not anymore (a Throwable is allowed to respect fewer rules than an Exception).

Ideas please?


Andrei

On 1/11/11 1:57 AM, Don Clugston wrote:
> I believe I have got TDPL exception chaining working correctly using
> Windows Structured Exception Handling.
> (This was far from easy!)
> Central to making chaining work correctly, is that chaining must only occur
> when a collision occurs (not merely when two exceptions are in flight,
> because one may be caught before it has any effect on the other). This
> means that multiple chains of exceptions
> may be in flight at any given time.
> My code works in all nasty corner cases I've tested, including
> multi-level collisions,
> where two exceptions collide in a function, then collide again with an
> even earlier exception chain in a finally block in a different function.
>
> So the general scheme appears to work.
> But, there's something I'm unclear about. When should chained
> exceptions be catchable?
> They are very nasty creatures, and you really want to know when they happen.
> Presumably, an AssertError which occurs while processing an
> FileException, should not be silently chained
> and caught in the FileException.
> In fact, should a chain containing an Error be catchable at all?
> (If not, it still has to at least be catchable in the catchall handler
> that wraps main()).
> Many other schemes are possible, but I think it's important that the
> rules remain simple.
>
> One simple solution would be to make chained exceptions only catchable
> by catch(Throwable).
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos
January 11, 2011
I've thought about this a bit more. Another simple rule is, that an
exception chain can be caught if  and only if every exception in that
chain can be caught.
So, for example,
catch(FileException) will catch multiple file exceptions.
catch(Exception) will catch any exception (but not Errors).
catch(Throwable) catches Errors as well.

I went ahead and implemented this. Everythings seems to Just Work. Will check it in shortly.


On 11 January 2011 18:30, Andrei Alexandrescu <andrei at erdani.com> wrote:
> Wow, this is incredible news!
>
> Thanks Don for working on this. Solid exception handling is a huge selling point for D.
>
> Regarding collateral throwables that are not Exception, good point (and I agree that the solution should be simple). TDPL doesn't discuss that issue, but it says that the initially-thrown exception is the "boss" and that everybody follows, so a possible design is to simply make the Throwable part of the chain.
>
> I'd want to have chained exceptions still catchable by catch (Exception) because it would be a first to have the contents of the data influence its type. As far as the type system is concerned, catch (Exception) should catch Exceptions, whether or not they have a tail.
>
> One possibility would be to move the Throwable to the front of the list. This also has its issues, for example the stack is unwound for a while and then not anymore (a Throwable is allowed to respect fewer rules than an Exception).
>
> Ideas please?
>
>
> Andrei
>
> On 1/11/11 1:57 AM, Don Clugston wrote:
>>
>> I believe I have got TDPL exception chaining working correctly using
>> Windows Structured Exception Handling.
>> (This was far from easy!)
>> Central to making chaining work correctly, is that chaining must only
>> occur
>> when a collision occurs (not merely when two exceptions are in flight,
>> because one may be caught before it has any effect on the other). This
>> means that multiple chains of exceptions
>> may be in flight at any given time.
>> My code works in all nasty corner cases I've tested, including
>> multi-level collisions,
>> where two exceptions collide in a function, then collide again with an
>> even earlier exception chain in a finally block in a different function.
>>
>> So the general scheme appears to work.
>> But, there's something I'm unclear about. When should chained
>> exceptions be catchable?
>> They are very nasty creatures, and you really want to know when they
>> happen.
>> Presumably, an AssertError which occurs while processing an
>> FileException, should not be silently chained
>> and caught in the FileException.
>> In fact, should a chain containing an Error be catchable at all?
>> (If not, it still has to at least be catchable in the catchall handler
>> that wraps main()).
>> Many other schemes are possible, but I think it's important that the
>> rules remain simple.
>>
>> One simple solution would be to make chained exceptions only catchable
>> by catch(Throwable).
>> _______________________________________________
>> phobos mailing list
>> phobos at puremagic.com
>> http://lists.puremagic.com/mailman/listinfo/phobos
>
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos
>
January 11, 2011
I think this is a good rule.  As you say, it is simple and easy to understand, and it ensures no critical exceptions are "lost" somewhere in a chain.  I would be very dissatisfied if my program throws an Error and it is caught (indirectly) by a catch(FileException) clause.

-Lars



On Tue, 2011-01-11 at 21:31 +0100, Don Clugston wrote:
> I've thought about this a bit more. Another simple rule is, that an
> exception chain can be caught if  and only if every exception in that
> chain can be caught.
> So, for example,
> catch(FileException) will catch multiple file exceptions.
> catch(Exception) will catch any exception (but not Errors).
> catch(Throwable) catches Errors as well.
> 
> I went ahead and implemented this. Everythings seems to Just Work. Will check it in shortly.
> 
> 
> On 11 January 2011 18:30, Andrei Alexandrescu <andrei at erdani.com> wrote:
> > Wow, this is incredible news!
> >
> > Thanks Don for working on this. Solid exception handling is a huge selling point for D.
> >
> > Regarding collateral throwables that are not Exception, good point (and I agree that the solution should be simple). TDPL doesn't discuss that issue, but it says that the initially-thrown exception is the "boss" and that everybody follows, so a possible design is to simply make the Throwable part of the chain.
> >
> > I'd want to have chained exceptions still catchable by catch (Exception) because it would be a first to have the contents of the data influence its type. As far as the type system is concerned, catch (Exception) should catch Exceptions, whether or not they have a tail.
> >
> > One possibility would be to move the Throwable to the front of the list. This also has its issues, for example the stack is unwound for a while and then not anymore (a Throwable is allowed to respect fewer rules than an Exception).
> >
> > Ideas please?
> >
> >
> > Andrei
> >
> > On 1/11/11 1:57 AM, Don Clugston wrote:
> >>
> >> I believe I have got TDPL exception chaining working correctly using
> >> Windows Structured Exception Handling.
> >> (This was far from easy!)
> >> Central to making chaining work correctly, is that chaining must only
> >> occur
> >> when a collision occurs (not merely when two exceptions are in flight,
> >> because one may be caught before it has any effect on the other). This
> >> means that multiple chains of exceptions
> >> may be in flight at any given time.
> >> My code works in all nasty corner cases I've tested, including
> >> multi-level collisions,
> >> where two exceptions collide in a function, then collide again with an
> >> even earlier exception chain in a finally block in a different function.
> >>
> >> So the general scheme appears to work.
> >> But, there's something I'm unclear about. When should chained
> >> exceptions be catchable?
> >> They are very nasty creatures, and you really want to know when they
> >> happen.
> >> Presumably, an AssertError which occurs while processing an
> >> FileException, should not be silently chained
> >> and caught in the FileException.
> >> In fact, should a chain containing an Error be catchable at all?
> >> (If not, it still has to at least be catchable in the catchall handler
> >> that wraps main()).
> >> Many other schemes are possible, but I think it's important that the
> >> rules remain simple.
> >>
> >> One simple solution would be to make chained exceptions only catchable
> >> by catch(Throwable).
> >> _______________________________________________
> >> phobos mailing list
> >> phobos at puremagic.com
> >> http://lists.puremagic.com/mailman/listinfo/phobos
> >
> > _______________________________________________
> > phobos mailing list
> > phobos at puremagic.com
> > http://lists.puremagic.com/mailman/listinfo/phobos
> >
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos


January 11, 2011
On Tuesday, January 11, 2011 12:44:51 Lars Tandle Kyllingstad wrote:
> I think this is a good rule.  As you say, it is simple and easy to understand, and it ensures no critical exceptions are "lost" somewhere in a chain.  I would be very dissatisfied if my program throws an Error and it is caught (indirectly) by a catch(FileException) clause.

I concur. That would seem to be a good approach. Errors definitely shouldn't be buried just because of an exception chain, but if it's just a bunch of chained Exceptions, you should still be able to catch them.

- Jonathan M Davis
January 11, 2011
On Jan 11, 2011, at 1:57 AM, Don Clugston wrote:
> 
> So the general scheme appears to work.
> But, there's something I'm unclear about. When should chained
> exceptions be catchable?
> They are very nasty creatures, and you really want to know when they happen.
> Presumably, an AssertError which occurs while processing an
> FileException, should not be silently chained
> and caught in the FileException.
> In fact, should a chain containing an Error be catchable at all?

Tricky question.  According to TDPL, this is all collateral damage, so I'd expect the chain to be caught just fine.  Worse is something like this:

void fnA() nothrow {
    try {
        fnB(); // documented to throw OopsException
    } catch (OopsException e) {}
}

void fnB() {
    scope(exit) closeFile();
    throw new OopsException;
}

void closeFile() {
    throw new UnableToCloseFileException;
}

Is this code correct or does it violate the nothrow guarantee?
January 11, 2011
I don't think that's helpful. It complicates the flow a lot because now understanding how the program acts depends not on the types anymore, but on what happens dynamically. Makes it more difficult, not easier, to write robust code.

If I throw a FileException, I must catch a FileException with catch(FileException) regardless of what collateral exceptions have happened.


Andrei

On 1/11/11 12:31 PM, Don Clugston wrote:
> I've thought about this a bit more. Another simple rule is, that an
> exception chain can be caught if  and only if every exception in that
> chain can be caught.
> So, for example,
> catch(FileException) will catch multiple file exceptions.
> catch(Exception) will catch any exception (but not Errors).
> catch(Throwable) catches Errors as well.
>
> I went ahead and implemented this. Everythings seems to Just Work. Will check it in shortly.
>
>
> On 11 January 2011 18:30, Andrei Alexandrescu<andrei at erdani.com>  wrote:
>> Wow, this is incredible news!
>>
>> Thanks Don for working on this. Solid exception handling is a huge selling point for D.
>>
>> Regarding collateral throwables that are not Exception, good point (and I agree that the solution should be simple). TDPL doesn't discuss that issue, but it says that the initially-thrown exception is the "boss" and that everybody follows, so a possible design is to simply make the Throwable part of the chain.
>>
>> I'd want to have chained exceptions still catchable by catch (Exception) because it would be a first to have the contents of the data influence its type. As far as the type system is concerned, catch (Exception) should catch Exceptions, whether or not they have a tail.
>>
>> One possibility would be to move the Throwable to the front of the list. This also has its issues, for example the stack is unwound for a while and then not anymore (a Throwable is allowed to respect fewer rules than an Exception).
>>
>> Ideas please?
>>
>>
>> Andrei
>>
>> On 1/11/11 1:57 AM, Don Clugston wrote:
>>>
>>> I believe I have got TDPL exception chaining working correctly using
>>> Windows Structured Exception Handling.
>>> (This was far from easy!)
>>> Central to making chaining work correctly, is that chaining must only
>>> occur
>>> when a collision occurs (not merely when two exceptions are in flight,
>>> because one may be caught before it has any effect on the other). This
>>> means that multiple chains of exceptions
>>> may be in flight at any given time.
>>> My code works in all nasty corner cases I've tested, including
>>> multi-level collisions,
>>> where two exceptions collide in a function, then collide again with an
>>> even earlier exception chain in a finally block in a different function.
>>>
>>> So the general scheme appears to work.
>>> But, there's something I'm unclear about. When should chained
>>> exceptions be catchable?
>>> They are very nasty creatures, and you really want to know when they
>>> happen.
>>> Presumably, an AssertError which occurs while processing an
>>> FileException, should not be silently chained
>>> and caught in the FileException.
>>> In fact, should a chain containing an Error be catchable at all?
>>> (If not, it still has to at least be catchable in the catchall handler
>>> that wraps main()).
>>> Many other schemes are possible, but I think it's important that the
>>> rules remain simple.
>>>
>>> One simple solution would be to make chained exceptions only catchable
>>> by catch(Throwable).
>>> _______________________________________________
>>> phobos mailing list
>>> phobos at puremagic.com
>>> http://lists.puremagic.com/mailman/listinfo/phobos
>>
>> _______________________________________________
>> phobos mailing list
>> phobos at puremagic.com
>> http://lists.puremagic.com/mailman/listinfo/phobos
>>
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos
January 11, 2011
On 1/11/11 12:44 PM, Lars Tandle Kyllingstad wrote:
> I think this is a good rule.  As you say, it is simple and easy to understand, and it ensures no critical exceptions are "lost" somewhere in a chain.  I would be very dissatisfied if my program throws an Error and it is caught (indirectly) by a catch(FileException) clause.

But that's not the alternative. There are several possible alternatives, one of which being that everything is just like until now. That _is_ the simplest almost by definition. We can discuss whether we can improve on that decision, but it is the simplest baseline.

I agree that throwing an Error brings a serious issue to the table. But throwing a DatabaseException while a FileException is already unwinding the stack should not transform what's happening. At the end of the day, the FileException was there "first".


Andrei
January 11, 2011
A nothrow function must either call only nothrow functions or catch(Exception) for all others. Anything else shouldn't compile.

Andrei

On 1/11/11 2:36 PM, Sean Kelly wrote:
> On Jan 11, 2011, at 1:57 AM, Don Clugston wrote:
>>
>> So the general scheme appears to work.
>> But, there's something I'm unclear about. When should chained
>> exceptions be catchable?
>> They are very nasty creatures, and you really want to know when they happen.
>> Presumably, an AssertError which occurs while processing an
>> FileException, should not be silently chained
>> and caught in the FileException.
>> In fact, should a chain containing an Error be catchable at all?
>
> Tricky question.  According to TDPL, this is all collateral damage, so I'd expect the chain to be caught just fine.  Worse is something like this:
>
> void fnA() nothrow {
>      try {
>          fnB(); // documented to throw OopsException
>      } catch (OopsException e) {}
> }
>
> void fnB() {
>      scope(exit) closeFile();
>      throw new OopsException;
> }
>
> void closeFile() {
>      throw new UnableToCloseFileException;
> }
>
> Is this code correct or does it violate the nothrow guarantee?
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos
January 11, 2011
On Tuesday, January 11, 2011 15:50:52 Andrei Alexandrescu wrote:
> I don't think that's helpful. It complicates the flow a lot because now understanding how the program acts depends not on the types anymore, but on what happens dynamically. Makes it more difficult, not easier, to write robust code.
> 
> If I throw a FileException, I must catch a FileException with catch(FileException) regardless of what collateral exceptions have happened.

I agree as long as it's other Exceptions that have been thrown. But Errors? Aren't they typically supposed to kill your program?

- Jonathan M Davis
« First   ‹ Prev
1 2 3