View mode: basic / threaded / horizontal-split · Log in · Help
May 31, 2012
Re: Exception/Error division in D
On Thursday, May 31, 2012 08:26:18 Jacob Carlborg wrote:
> On 2012-05-30 23:16, Jonathan M Davis wrote:
> > You can catch them to print out additional information or whatever is
> > useful to generate more information about the Error. In fact, just what
> > the Error gives you is already more useful: message, file, line number,
> > stack trace, etc. That alone makes an Error more useful than a halt
> > instruction.
> 
> If I recall correctly you has been arguing that Errors shouldn't be
> catchable, as they are today, and this needed to be fixed. Hmm, or was
> that Steven.

No. I haven't been arguing that. It's not particularly safe to catch Errors, 
but they're catchable on purpose. It's catching and _handling_ an Error that's 
generally a bad idea - that and continuing to execute after catching the Error 
rather than letting the program terminate. It may be appropriate in very rare 
examples where the programmer knows what they're doing, but in general, 
catching them to do much beyond print out additional information or maybe do 
some absolutely critical cleanup is a bad idea.

The real question is whether any cleanup should be attempted on Error (i.e. 
destructors, scope statements, and finally blocks). Running that code is risky 
when an Error occurs, because the program is in an invalid state. It's 
possible that running it could actually do damage of some variety, depending 
on the state of the program and what the cleanup does. But skipping all of 
that cleanup can be a problem too, since that cleanup generally needs to be 
done. Depending on what the Error was, the cleanup may actually work and 
therefore leave the program in a less invalid state. So, it's a question of 
whether attempting cleanup in an invalid state or skipping cleanup in an 
invalid state is riskier. Per Walter, there's no guarantee that that cleanup 
will occur, but with the current implementation it almost always does.

> > You can catch them to attempt explicit cleanup that absolutely must be
> > done
> > for whatever reason (with the knowledge that it's potentially dangerous to
> > do that cleanup due to the Error).
> > 
> > You can catch them in very controlled circumstances where you know that
> > continuing is safe (obviously this isn't the sort of thing that you do in
> > @safe code). For instance, in some restricted cases, that could be done
> > with an OutOfMemoryError. But when you do that sort of thing you have to
> > catch the Error _very_ close to the throw point and be sure that there's
> > no cleanup code in between. It only works when you can guarantee yourself
> > that the program state is not being compromised by the Error, and you're
> > able to guarantee that continuing from the catch point is safe. That
> > works in some cases with AssertError in unit test code but becomes
> > problematic as such code becomes more complex.
> 
> I'm mostly interested in letting the user know something went wrong and
> then exit the application.

That would be the most typical use case for catching an Error and certainly is 
the least risky of the reasons that you might do it.

- Jonathan M Davis
May 31, 2012
Re: Exception/Error division in D
On 2012-05-31 08:39, Jonathan M Davis wrote:

> No. I haven't been arguing that. It's not particularly safe to catch Errors,
> but they're catchable on purpose. It's catching and _handling_ an Error that's
> generally a bad idea - that and continuing to execute after catching the Error
> rather than letting the program terminate. It may be appropriate in very rare
> examples where the programmer knows what they're doing, but in general,
> catching them to do much beyond print out additional information or maybe do
> some absolutely critical cleanup is a bad idea.
>
> The real question is whether any cleanup should be attempted on Error (i.e.
> destructors, scope statements, and finally blocks). Running that code is risky
> when an Error occurs, because the program is in an invalid state. It's
> possible that running it could actually do damage of some variety, depending
> on the state of the program and what the cleanup does. But skipping all of
> that cleanup can be a problem too, since that cleanup generally needs to be
> done. Depending on what the Error was, the cleanup may actually work and
> therefore leave the program in a less invalid state. So, it's a question of
> whether attempting cleanup in an invalid state or skipping cleanup in an
> invalid state is riskier. Per Walter, there's no guarantee that that cleanup
> will occur, but with the current implementation it almost always does.

Ok, I'm sorry if I misunderstood you or confused you with someone else.

-- 
/Jacob Carlborg
May 31, 2012
Re: Exception/Error division in D
Walter Bright wrote:
> On 5/30/2012 2:32 AM, Don Clugston wrote:
> >In fact, generally, the point of an AssertError is to prevent the program from
> >entering an invalid state.
> 
> It's already in an invalid state when the assert fails, by definition.
> 
> It is not recoverable. The only option is a more or less graceful shutdown.

How do I do a graceful shutdown if finally and scope is not guaranteed
to be executed? Assuming onAssertError, etc. is of no use because I need
to perform different shutdowns due to having different cases or if I
defined my own Error, let's say for some device.

Jens
May 31, 2012
Re: Exception/Error division in D
Le 31/05/2012 04:10, Walter Bright a écrit :
> On 5/30/2012 5:58 PM, Kapps wrote:
>> All code has bugs in it. It's nice being notified about it and all,
>> but if you
>> release a server application, and it crashes every single time it
>> encounters any
>> bug... well, your customers will not have a fun time.
>
> The correct response to a server app crashing is to restart it, not
> attempt to keep a program in an invalid state running.
>

Should I mention to restart it AFTER A GRACEFUL SHUTDOWN ?
May 31, 2012
Re: Exception/Error division in D
Le 31/05/2012 03:06, Artur Skawina a écrit :
> On 05/31/12 00:21, Jonathan M Davis wrote:
>> Now, it's perfectly possible to design code which never checks for null
>> pointers and if a null pointer is dereferenced throws an Exception and
>> attempts to recover from it (assuming that it's possible to detect the
>> dereference and throw at that point, which AFAIK is impossible with segfaults
>> - maybe it could be done on Windows with its Access Violations, but segfaults
>> trigger a signal handler, and you're screwed at that point). But writing code
>
> No, it's easily recoverable. That does not mean however that it would be a good
> idea to map segfaults to exceptions as a language feature. And dereferencing a
> null pointer is *not* guaranteed to trap, all you need is a large enough offset
> and you will get silent data corruption.
>
>     int i = 42;
>     auto j = cast(size_t)&i;
>
>     ubyte* p = null;
>     p[j] = 13;
>     assert(i!=42); // oops
>
> artur

Most system protect at least the first page (the first 4Kb ). For other 
it is doable within druntime with page protection.

For bigger stuff, they have to be forbidden in @safe code or runtime 
check should be added.
May 31, 2012
Re: Exception/Error division in D
Le 31/05/2012 04:17, Walter Bright a écrit :
> On 5/30/2012 8:05 AM, Steven Schveighoffer wrote:
>> I'd classify errors/exceptions into three categories:
>>
>> 1. corruption/segfault -- not recoverable under any reasonable
>> circumstances.
>> Special cases exist (such as a custom paging mechanism).
>> 2. program invariant errors (i.e. assert errors) -- Recovery is not
>> defined by
>> the runtime, so you must do it manually. Any decision the runtime
>> makes will be
>> arbitrary, and could be wrong.
>> 3. try/catch exceptions -- these are planned for and *expected* to
>> occur because
>> the program cannot control it's environment. e.g. EOF when none was
>> expected.
>
>
> A recoverable exception is NOT a logic bug in your program, which is why
> it is recoverable.
>
> If there is recovery possible from a particular assert error, then you
> are using asserts incorrectly. Assert errors occur because your program
> has entered an unanticipated, invalid state. There's no such thing as
> knowing how to put it back into a valid state, because you don't know
> where it went wrong and how much is corrupted, etc.

A failure in database component should prevent me from close a network 
connection properly in an unrelated part of the program.

This is called failing gracefully. And this highly recommended, and you 
KNOW that the system will fail at some point.
May 31, 2012
Re: Exception/Error division in D
Le 31/05/2012 00:21, Jonathan M Davis a écrit :
> On Thursday, May 31, 2012 00:01:16 deadalnix wrote:
>> Le 30/05/2012 17:29, Don Clugston a écrit :
>>> There's a big difference. A segfault is a machine error. The integrity
>>> of the machine model has been violated, and the machine is in an
>>> out-of-control state. In particular, the stack may be corrupted, so
>>> stack unwinding may not be successful.
>>>
>>> But, in an assert error, the machine is completely intact; the error is
>>> at a higher level, which does not interfere with stack unwinding.
>>>
>>> Damage is possible only if you've written your destructors/finally code
>>> extremely poorly. Note that, unlike C++, it's OK to throw a new Error or
>>> Exception from inside a destructor.
>>> But with (say) a stack overflow, you don't necessarily know what code is
>>> being executed. It could do anything.
>>
>> Most segfault are null deference or unintizialized pointer deference.
>> Both are recoverable.
>
> If you dereferenced a null pointer, it's a bug in your code. Your code is
> assuming that the pointer was non-null, which was obviously incorrect, because
> it was null. That's _not_ recoverable in the general case. Your code was
> obviously written with the assumption that the pointer was non-null, so your
> code is wrong, and so continuing to execute it makes no sense, because it's in
> an invalid state and could do who-knows-what. If there's any possibility of a
> pointer being null, the correct thing to do is to check it before
> dereferencing it. If you don't, it's bug.
>

I want to remind you that the subject is knowing if scope and finally 
block should be triggered by errors.

Such blocks are perfectly safe to execute in such a situation.

Additionally, I may want to abort the current operation, but not the 
whole program. This is doable on such an error.

> Now, it's perfectly possible to design code which never checks for null
> pointers and if a null pointer is dereferenced throws an Exception and
> attempts to recover from it (assuming that it's possible to detect the
> dereference and throw at that point, which AFAIK is impossible with segfaults
> - maybe it could be done on Windows with its Access Violations, but segfaults
> trigger a signal handler, and you're screwed at that point).

Well, I have a pull request in druntime that do exactly what you claim 
is impossible.
May 31, 2012
Re: Exception/Error division in D
On Wed, 30 May 2012 22:36:50 +0100, Jonathan M Davis <jmdavisProg@gmx.com>  
wrote:
> In general, a segfault is definitely worse, but logic errors can_ be  
> just as bad in terms of the damage that they can do (especially in  
> cmparison with
> segfaults caused by null pointers as opposed to those caused by memory
> corruption). It all depends on what the logic error is and what happens  
> if you try and continue with the program in such a state.

In fact.. a logic error - or rather an assert triggered by a bad argument  
or similar may be the result of memory corruption which was undetected  
i.e. buffer overflow on the stack overwriting an integer passed to a  
function which asserts it is within a range, and it isn't.  So, you can't  
even be sure that logic errors aren't caused by memory corruption.

R

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
May 31, 2012
Re: Exception/Error division in D
On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote:
> On 5/30/2012 8:05 AM, Steven Schveighoffer wrote:
>> I'd classify errors/exceptions into three categories:
>>
>> 1. corruption/segfault -- not recoverable under any reasonable 
>> circumstances.
>> Special cases exist (such as a custom paging mechanism).
>> 2. program invariant errors (i.e. assert errors) -- Recovery 
>> is not defined by
>> the runtime, so you must do it manually. Any decision the 
>> runtime makes will be
>> arbitrary, and could be wrong.
>> 3. try/catch exceptions -- these are planned for and 
>> *expected* to occur because
>> the program cannot control it's environment. e.g. EOF when 
>> none was expected.
>
>
> A recoverable exception is NOT a logic bug in your program, 
> which is why it is recoverable.
>
> If there is recovery possible from a particular assert error, 
> then you are using asserts incorrectly.

I think this is a key point.  Asserts are there to verify and 
debug program logic, they are not part of the logic itself.  They 
are a useful tool for the programmer, nothing more.  
Specifically, asserts are NOT an error handling mechanism!

If you compile the code with -release (which is often the case 
for production code), the asserts won't even be included.  
Therefore, when designing the error handling mechanisms for a 
program, one should just pretend the asserts aren't there.  There 
is no point in writing code which shuts down "gracefully" when it 
is compiled without -release, but trudges on in an invalid state 
when compiled in release mode.  Then you should have been using 
enforce() instead.

-Lars
May 31, 2012
Re: Exception/Error division in D
Le 31/05/2012 11:23, Lars T. Kyllingstad a écrit :
> On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote:
>> On 5/30/2012 8:05 AM, Steven Schveighoffer wrote:
>>> I'd classify errors/exceptions into three categories:
>>>
>>> 1. corruption/segfault -- not recoverable under any reasonable
>>> circumstances.
>>> Special cases exist (such as a custom paging mechanism).
>>> 2. program invariant errors (i.e. assert errors) -- Recovery is not
>>> defined by
>>> the runtime, so you must do it manually. Any decision the runtime
>>> makes will be
>>> arbitrary, and could be wrong.
>>> 3. try/catch exceptions -- these are planned for and *expected* to
>>> occur because
>>> the program cannot control it's environment. e.g. EOF when none was
>>> expected.
>>
>>
>> A recoverable exception is NOT a logic bug in your program, which is
>> why it is recoverable.
>>
>> If there is recovery possible from a particular assert error, then you
>> are using asserts incorrectly.
>
> I think this is a key point. Asserts are there to verify and debug
> program logic, they are not part of the logic itself. They are a useful
> tool for the programmer, nothing more. Specifically, asserts are NOT an
> error handling mechanism!
>
> If you compile the code with -release (which is often the case for
> production code), the asserts won't even be included. Therefore, when
> designing the error handling mechanisms for a program, one should just
> pretend the asserts aren't there. There is no point in writing code
> which shuts down "gracefully" when it is compiled without -release, but
> trudges on in an invalid state when compiled in release mode. Then you
> should have been using enforce() instead.
>
> -Lars
>

Your are lost in the details of that specific case. assert is a very 
specific issue. What is discussed here is much broader.
1 2 3 4 5 6 7 8 9
Top | Discussion index | About this forum | D home