runtime hook for Crash on Error

Don Clugston dac at nospam.com
Wed Jun 6 08:02:44 PDT 2012


On 05/06/12 17:44, Jonathan M Davis wrote:
> On Tuesday, June 05, 2012 13:57:14 Don Clugston wrote:
>> On 05/06/12 09:07, Jonathan M Davis wrote:
>>> On Tuesday, June 05, 2012 08:53:16 Don Clugston wrote:
>>>> On 04/06/12 21:29, Steven Schveighoffer wrote:
>>>>> On Mon, 04 Jun 2012 06:20:56 -0400, Don Clugston<dac at nospam.com>   wrote:
>>>>>> 1. There exist cases where you cannot know why the assert failed.
>>>>>> 2. Therefore you never know why an assert failed.
>>>>>> 3. Therefore it is not safe to unwind the stack from a nothrow
>>>>>> function.
>>>>>>
>>>>>> Spot the fallacies.
>>>>>>
>>>>>> The fallacy in moving from 2 to 3 is more serious than the one from 1
>>>>>> to 2: this argument is not in any way dependent on the assert occuring
>>>>>> in a nothrow function. Rather, it's an argument for not having
>>>>>> AssertError at all.
>>>>>
>>>>> I'm not sure that is the issue here at all. What I see is that the
>>>>> unwinding of the stack is optional, based on the assumption that there's
>>>>> no "right" answer.
>>>>>
>>>>> However, there is an underlying driver for not unwinding the stack --
>>>>> nothrow. If nothrow results in the compiler optimizing out whatever
>>>>> hooks a function needs to properly unwind itself (my limited
>>>>> understanding is that this helps performance), then there *is no
>>>>> choice*, you can't properly unwind the stack.
>>>>>
>>>>> -Steve
>>>>
>>>> No, this whole issue started because the compiler currently does do
>>>> unwinding whenever it can. And Walter claimed that's a bug, and it
>>>> should be explicitly disabled.
>>>>
>>>> It is, in my view, an absurd position. AFAIK not a single argument has
>>>> been presented in favour of it. All arguments have been about "you
>>>> should never unwind Errors".
>>>
>>> It's quite clear that we cannot completely, correctly unwind the stack in
>>> the face of Errors.
>>
>> Well that's a motherhood statement. Obviously in the face of extreme
>> memory corruption you can't guarantee *any* code is valid.
>> The *main* reason why stack unwinding would not be possible is if
>> nothrow intentionally omits stack unwinding code.
>
> It's not possible precisely because of nothrow.


nothrow only means 'does not throw Exceptions'. It doesn't mean 'does 
not throw Errors'.
Therefore, given:

int foo() nothrow { ...}


try
{
    foo();
} catch (Error e)
{
   ...
}

even though there are no throw statements inside foo(), the compiler is 
NOT permitted to remove the catch(Error), whereas it could remove 
catch(Exception).

The problem is 'finally' clauses. Are they called only on Exception, or 
on Exception and Error?

>>> Regardless, I think that there are a number of people in this thread who
>>> are mistaken in how recoverable they think Errors and/or segfaults are,
>>> and they seem to be the ones pushing the hardest for full stack unwinding
>>> on the theory that they could somehow ensure safe recovery and a clean
>>> shutdown when an Error occurs, which is almost never possible, and
>>> certainly isn't possible in the general case.
>>>
>>> - Jonathan M Davis
>>
>> Well I'm pushing it because I implemented it (on Windows).
>>
>> I'm less knowledgeable about what happens on other systems, but know
>> that on Windows, the whole system is far, far more robust than most
>> people on this thread seem to think.
>>
>> I can't see *any* problem with executing catch(Error) clauses. I cannot
>> envisage a situation where that can cause a problem. I really cannot.
>
> In many cases, it's probably fine, but if the program is in a bad enough state
> that an Error is thrown, then you can't know for sure that any particular such
> block will execute properly (memory corruption being the extreme case), and if
> it doesn't run correctly, then it could make things worse (e.g. writing
> invalid data to a file, corrupting that file). Also, if the stack is not unwound
> perfectly (as nothrow prevents), then the program's state will become
> increasingly invalid the farther that the program gets from the throw point,
> which will increase the chances of cleanup code functioning incorrectly, as
> any assumptions that they've made about the program state are increasingly
> likely to be wrong (as well as it being increasingly likely that the variables
> that they operate on no longer being valid).
>
> A lot of it comes down to worst case vs typical case. In the typical case, the
> code causing the Error is isolated enough and the code doing the cleanup is
> self-contained enough that trying to unwind the stack as much as possible will
> result in more correct behavior than skipping it all. But in the worst case,
> you can't rely on running any code being safe, because the state of the
> program is very much invalid, in which case, it's better to kill the program
> ASAP. Walter seems to subscribe to the approach that it's best to assume the
> worst case (e.g. that an assertion failure indicates horrible memory
> corruption), and always have Errors function that way, whereas others
> subscribe to the approach that things are almost never that bad, so we should
> just assume that they aren't, since skipping all of that cleanup causes other
> problems.

I believe I now understand the root issue behind this dispute.

Consider:

if (x) throw new FileError;

if (x) throw new FileException;

What is the difference between these two, from the point of view of the 
compiler? Practically nothing. Only the name is different.
There is absolutely no difference in the validity of the machine state 
when executing the first, rather than the second.

In both cases it is possible that something has gone horribly wrong; 
it's also possible that it's a superficial problem.

The difference between Error and Exception is a matter of *convention*.

Now, what people have been pointing out is that *even with things like 
null pointer exceptions* there are still cases where the machine state 
has remained valid.

Now, we can say that when an Error is thrown, the machine is in an 
invalid state *by definition*, regardless of whether it really is, or 
not. If we do this, then Walters statements about catching AssertErrors 
become valid, but for a different reason.

When you have thrown an Error, you've told the compiler that the machine 
is in an invalid state. Catching it and continuing is wrong not because 
the machine is unstable (which might be true, or might not); rather it's 
wrong because it's logically inconsistent: by throwing an Error you've 
told the compiler that it is not recoverable, but by catching it, you've 
also told it that it is recoverable!

If we chose to say that Error means that the machine is in an invalid 
state, there are still a couple of issues:

(1) How to deal with cases where the compiler generates an Error, but 
you know that the machine state is still valid, and you want to supress 
the Error and continue.

I think Steven's point near the start of the thread was excellent: in 
the cases where recovery is possible, it is almost always extremely 
close to the point where the Error was generated.

(2) Does it make sense to run finally clauses on Error, if we are saying 
that the machine state is invalid?

Ie, at present they are finally_Throwable clauses, should they instead 
be finally_Exception clauses?

I cannot see any way in which it makes sense to run them if they're in a 
throwable function, but not if they are in a nothrow function.

If we take the view that Error by definition implies an invalid machine 
state, then I don't think they should run at all.

But noting that in a significant fraction of cases, the machine state 
isn't actually invalid, I think it can be reasonable to provide a 
mechanism to make them be run.

BTW its worth noting that cleanup is not necessarily performed even in 
the Exception case. If an exception is thrown while processing a finally 
clause (eg, inside a destructor) then the destructor didn't completely 
run. C++ just aborts the program if this happens. We've got exception 
chaining so that case is well defined, and can be detected, nonetheless 
it's got a lot of similarities to the Error case.


More information about the Digitalmars-d mailing list