View mode: basic / threaded / horizontal-split · Log in · Help
May 24, 2012
Exception/Error division in D
Let's talk about an abstract situation without caring about breaking 
existing code, current docs, implementation etc.

Definitions:
* an Exception is something that tigers scope guards and executes 
catch/finally blocks if thrown;
* an Error is something that doesn't do it.

As a result _we can't do any clean-up if an Error is thrown_ because 
scope guards and catch/finally blocks aren't executed and the program is 
in invalid state because of this. Of course it's theoretically possible 
to code without scope guards and catch/finally blocks but isn't 
applicably for a real project. E.g. in some editor if and Error is 
thrown there is no ability to save opened documents.


Main Question: What do you think can be an Error?

Can "Integer Divide by Zero" be an Error? Definitely, not.

Can "Access Violation" be an Error? No, because it's very common to 
access a field/virtual member function of a null object.

Can "Out of memory" be an Error? No, because e.g. if I read a user file 
that require me to create a large array (> 100 MiB, e.g.) I don't want 
to crash, but just tell, that "Dear user, the file can't be opened 
because it requires..."


So what am I think can be an Error? IMHO, nothing. Because throwing 
everything can indicate that program in very good/terribly bad state and 
compiler doesn't know anything about that. And because fatal error is 
fatal the program should just try to print error and close instead of 
throwing something.


Let's now return to the real D world. Current implementation treats 
Errors as Exceptions for now. Documentation keeps silence. All listed 
"can't be an error" cases are Errors (and it's terrible).

So why do we has Exception/Error division in D? Because of nothrow. 
Personally I don't need nothrow for that high cost of making D unusable 
for me. Lets realize and solve Exception/Error problem and solve nothrow 
in the second place.


Related links:
http://forum.dlang.org/thread/1566418.J7qGkEti3s@lyonel
http://d.puremagic.com/issues/show_bug.cgi?id=8135
http://d.puremagic.com/issues/show_bug.cgi?id=8136
http://d.puremagic.com/issues/show_bug.cgi?id=8137


P.S.
By the way, the only problem I see in current implementation is a luck 
of "Object finalized" assertion ("True disposable objects (add 
"Finalized!" assertion)" NG thread that didn't interest anybody).

-- 
Денис В. Шеломовский
Denis V. Shelomovskij
May 24, 2012
Re: Exception/Error division in D
On May 24, 2012, at 3:27 AM, Denis Shelomovskij wrote:

> Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc.
> 
> Definitions:
> * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown;
> * an Error is something that doesn't do it.
> 
> As a result _we can't do any clean-up if an Error is thrown_ because scope guards and catch/finally blocks aren't executed and the program is in invalid state because of this. Of course it's theoretically possible to code without scope guards and catch/finally blocks but isn't applicably for a real project. E.g. in some editor if and Error is thrown there is no ability to save opened documents.
> 
> 
> Main Question: What do you think can be an Error?
> 
> Can "Integer Divide by Zero" be an Error? Definitely, not.
> 
> Can "Access Violation" be an Error? No, because it's very common to access a field/virtual member function of a null object.
> 
> Can "Out of memory" be an Error? No, because e.g. if I read a user file that require me to create a large array (> 100 MiB, e.g.) I don't want to crash, but just tell, that "Dear user, the file can't be opened because it requires..."
> 
> 
> So what am I think can be an Error? IMHO, nothing. Because throwing everything can indicate that program in very good/terribly bad state and compiler doesn't know anything about that. And because fatal error is fatal the program should just try to print error and close instead of throwing something.

I agree.  However, the benefit to having Error is so that nothrow can exist.  If every exception were considered recoverable then we'd have to throw out nothrow as well, since basically anything can generate an access violation, for example.  Or we could weaken nothrow so that it didn't even allow memory allocations, which would render it largely useless.  For what it's worth, the D runtime does do clean-up for both Errors and Exceptions today.  The only difference is codgen for scope statements and such inside nothrow functions--instead of being rewritten as try/finally the on-exit code is just inserted at the proper scope exit points.


> Let's now return to the real D world. Current implementation treats Errors as Exceptions for now. Documentation keeps silence. All listed "can't be an error" cases are Errors (and it's terrible).
> 
> So why do we has Exception/Error division in D? Because of nothrow. Personally I don't need nothrow for that high cost of making D unusable for me. Lets realize and solve Exception/Error problem and solve nothrow in the second place.

Seems you already know this.  Oops.  I'm inclined to agree, personally.  nothrow is less useful in D than in C++ because it's safe to throw from dtors in D (problems related to throwing from a finalizer during a GC collection aside--that's more an exception safety issue for the GC than anything else).
May 24, 2012
Re: Exception/Error division in D
I'd say, removing nothrow and Error from D would be a good idea. Everybody
throws Exception from anywhere. What would be the practical reason not to
do that (besides potentially breaking code)?

On Thu, May 24, 2012 at 9:51 PM, Sean Kelly <sean@invisibleduck.org> wrote:

> On May 24, 2012, at 3:27 AM, Denis Shelomovskij wrote:
>
> > Let's talk about an abstract situation without caring about breaking
> existing code, current docs, implementation etc.
> >
> > Definitions:
> > * an Exception is something that tigers scope guards and executes
> catch/finally blocks if thrown;
> > * an Error is something that doesn't do it.
> >
> > As a result _we can't do any clean-up if an Error is thrown_ because
> scope guards and catch/finally blocks aren't executed and the program is in
> invalid state because of this. Of course it's theoretically possible to
> code without scope guards and catch/finally blocks but isn't applicably for
> a real project. E.g. in some editor if and Error is thrown there is no
> ability to save opened documents.
> >
> >
> > Main Question: What do you think can be an Error?
> >
> > Can "Integer Divide by Zero" be an Error? Definitely, not.
> >
> > Can "Access Violation" be an Error? No, because it's very common to
> access a field/virtual member function of a null object.
> >
> > Can "Out of memory" be an Error? No, because e.g. if I read a user file
> that require me to create a large array (> 100 MiB, e.g.) I don't want to
> crash, but just tell, that "Dear user, the file can't be opened because it
> requires..."
> >
> >
> > So what am I think can be an Error? IMHO, nothing. Because throwing
> everything can indicate that program in very good/terribly bad state and
> compiler doesn't know anything about that. And because fatal error is fatal
> the program should just try to print error and close instead of throwing
> something.
>
> I agree.  However, the benefit to having Error is so that nothrow can
> exist.  If every exception were considered recoverable then we'd have to
> throw out nothrow as well, since basically anything can generate an access
> violation, for example.  Or we could weaken nothrow so that it didn't even
> allow memory allocations, which would render it largely useless.  For what
> it's worth, the D runtime does do clean-up for both Errors and Exceptions
> today.  The only difference is codgen for scope statements and such inside
> nothrow functions--instead of being rewritten as try/finally the on-exit
> code is just inserted at the proper scope exit points.
>
>
> > Let's now return to the real D world. Current implementation treats
> Errors as Exceptions for now. Documentation keeps silence. All listed
> "can't be an error" cases are Errors (and it's terrible).
> >
> > So why do we has Exception/Error division in D? Because of nothrow.
> Personally I don't need nothrow for that high cost of making D unusable for
> me. Lets realize and solve Exception/Error problem and solve nothrow in the
> second place.
>
> Seems you already know this.  Oops.  I'm inclined to agree, personally.
>  nothrow is less useful in D than in C++ because it's safe to throw from
> dtors in D (problems related to throwing from a finalizer during a GC
> collection aside--that's more an exception safety issue for the GC than
> anything else).




-- 
Bye,
Gor Gyolchanyan.
May 24, 2012
Re: Exception/Error division in D
On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskij  
<verylonglogin.reg@gmail.com> wrote:

> Let's talk about an abstract situation without caring about breaking  
> existing code, current docs, implementation etc.
>
> Definitions:
> * an Exception is something that tigers scope guards and executes  
> catch/finally blocks if thrown;
> * an Error is something that doesn't do it.

I'll give you a different definition:

* an Exception is something that can be handled far away from the context  
of the error, because the system can safely unwind the stack.
* an Error must be handled at the point the error occurred, or the program  
state is by definition invalid.

>
> As a result _we can't do any clean-up if an Error is thrown_ because  
> scope guards and catch/finally blocks aren't executed and the program is  
> in invalid state because of this. Of course it's theoretically possible  
> to code without scope guards and catch/finally blocks but isn't  
> applicably for a real project. E.g. in some editor if and Error is  
> thrown there is no ability to save opened documents.
>
>
> Main Question: What do you think can be an Error?
>
> Can "Integer Divide by Zero" be an Error? Definitely, not.

I agree with you there.  However, the problem isn't nearly as bad as you  
say.  The runtime doesn't actually deal with SIGFPE on Unix based systems,  
and most places where an "Error" is thrown, it's within asserts, which are  
compiled out in release code.

If you get one of these, you can handle the signal.  I think on Windows  
it's handled for you, but there should be a way to intercept this and do  
recovery.

> Can "Access Violation" be an Error? No, because it's very common to  
> access a field/virtual member function of a null object.

I'd argue this is unrecoverable.  Access Violation results from  
corruption, and the damage has already been done.  Even a null pointer  
access can be the cause of previous corruption.  There is no "recovery"  
from memory corruption.  There is no way to plan for it.

Now, if you want to handle it specifically for your program, that should  
be doable, at your own risk.  But there's no way throwing an Exception is  
a valid result.

> Can "Out of memory" be an Error? No, because e.g. if I read a user file  
> that require me to create a large array (> 100 MiB, e.g.) I don't want  
> to crash, but just tell, that "Dear user, the file can't be opened  
> because it requires..."

Right, out of memory is only an error if your program's invariant depends  
on the memory allocation.  You can plan for the above easily enough, but  
not realistically for all tasks and all library code that require  
allocation.

For example, let's say you are restructuring a hash table, and you  
reallocate some nodes.  You have half transferred over the old structure  
to the new structure, and you run out of memory.  How to recover from this?

In summary, I think we can adjust Windows divide by zero errors to allow  
you to handle them manually, the Posix version is fine (no Error is  
generated), access violations are unequivocally Errors, and out of memory  
should be fixable by making allocation routines that do not throw (just  
return null).

An interesting idea would be to allow a global "handler" for all errors.   
So if you throw an Exception, it unwinds the stack like normal.  If you  
throw an Error, it checks a handler to see if you want to handle that  
error (via registering a function/delegate), and if not handled throws  
Error without properly unwinding the stack.

-Steve
May 24, 2012
Re: Exception/Error division in D
On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote:

> On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskij <verylonglogin.reg@gmail.com> wrote:
> 
>> Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc.
>> 
>> Definitions:
>> * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown;
>> * an Error is something that doesn't do it.
> 
> I'll give you a different definition:
> 
> * an Exception is something that can be handled far away from the context of the error, because the system can safely unwind the stack.
> * an Error must be handled at the point the error occurred, or the program state is by definition invalid.

This is a good point.  OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack.


>> Can "Access Violation" be an Error? No, because it's very common to access a field/virtual member function of a null object.
> 
> I'd argue this is unrecoverable.  Access Violation results from corruption, and the damage has already been done.  Even a null pointer access can be the cause of previous corruption.  There is no "recovery" from memory corruption.  There is no way to plan for it.
> 
> Now, if you want to handle it specifically for your program, that should be doable, at your own risk.  But there's no way throwing an Exception is a valid result.

Right.  Recovery is potentially valid at the point of failure, not somewhere up the stack.


>> Can "Out of memory" be an Error? No, because e.g. if I read a user file that require me to create a large array (> 100 MiB, e.g.) I don't want to crash, but just tell, that "Dear user, the file can't be opened because it requires..."
> 
> Right, out of memory is only an error if your program's invariant depends on the memory allocation.  You can plan for the above easily enough, but not realistically for all tasks and all library code that require allocation.
> 
> For example, let's say you are restructuring a hash table, and you reallocate some nodes.  You have half transferred over the old structure to the new structure, and you run out of memory.  How to recover from this?

I think it's fair to expect code that allocates be exception-safe in the face of allocation errors.  I know I'm always very careful with containers so that an allocation failure doesn't result in corruption, for example.


> In summary, I think we can adjust Windows divide by zero errors to allow you to handle them manually, the Posix version is fine (no Error is generated), access violations are unequivocally Errors, and out of memory should be fixable by making allocation routines that do not throw (just return null).

It would be kind of cool if there were some sort of unified way to handle system-generated errors, though I don't know that this is practical.  signals sort of work on Windows, but I'm pretty sure the contextual sigaction stuff does not.


> An interesting idea would be to allow a global "handler" for all errors.  So if you throw an Exception, it unwinds the stack like normal.  If you throw an Error, it checks a handler to see if you want to handle that error (via registering a function/delegate), and if not handled throws Error without properly unwinding the stack.

^^ this
May 24, 2012
Re: Exception/Error division in D
On Thu, 24 May 2012 15:33:07 -0400, Sean Kelly <sean@invisibleduck.org>  
wrote:

> On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote:
>

>>> Can "Out of memory" be an Error? No, because e.g. if I read a user  
>>> file that require me to create a large array (> 100 MiB, e.g.) I don't  
>>> want to crash, but just tell, that "Dear user, the file can't be  
>>> opened because it requires..."
>>
>> Right, out of memory is only an error if your program's invariant  
>> depends on the memory allocation.  You can plan for the above easily  
>> enough, but not realistically for all tasks and all library code that  
>> require allocation.
>>
>> For example, let's say you are restructuring a hash table, and you  
>> reallocate some nodes.  You have half transferred over the old  
>> structure to the new structure, and you run out of memory.  How to  
>> recover from this?
>
> I think it's fair to expect code that allocates be exception-safe in the  
> face of allocation errors.  I know I'm always very careful with  
> containers so that an allocation failure doesn't result in corruption,  
> for example.

I don't think it's fair to expect *all* code to be able to safely recover  
from an out of memory exception.  I pretty much *never* write code that  
worries about out of memory errors.  One cannot always expect an operation  
involving hundreds of allocations to be atomic.

That being said, we should provide a mechanism so you can handle it, as  
it's reliably detectable and very recoverable in many situations.

-Steve
May 25, 2012
Re: Exception/Error division in D
On 2012-05-24 21:33, Sean Kelly wrote:

> This is a good point.  OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack.

You never feel you want to catch at the top level, print a sensible 
error message and then exit the application? Instead of the application 
just disappearing for user.

If the application can't print the error message it just fails in the 
same way as if you had not catch the error.

-- 
/Jacob Carlborg
May 29, 2012
Re: Exception/Error division in D
On May 24, 2012, at 11:50 PM, Jacob Carlborg wrote:

> On 2012-05-24 21:33, Sean Kelly wrote:
> 
>> This is a good point.  OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack.
> 
> You never feel you want to catch at the top level, print a sensible error message and then exit the application? Instead of the application just disappearing for user.

Well sure, but I wouldn't consider this recovery.
May 30, 2012
Re: Exception/Error division in D
Le 24/05/2012 12:27, Denis Shelomovskij a écrit :
> Let's talk about an abstract situation without caring about breaking
> existing code, current docs, implementation etc.
>
> Definitions:
> * an Exception is something that tigers scope guards and executes
> catch/finally blocks if thrown;
> * an Error is something that doesn't do it.
>
> As a result _we can't do any clean-up if an Error is thrown_ because
> scope guards and catch/finally blocks aren't executed and the program is
> in invalid state because of this. Of course it's theoretically possible
> to code without scope guards and catch/finally blocks but isn't
> applicably for a real project. E.g. in some editor if and Error is
> thrown there is no ability to save opened documents.
>
>
> Main Question: What do you think can be an Error?
>
> Can "Integer Divide by Zero" be an Error? Definitely, not.
>
> Can "Access Violation" be an Error? No, because it's very common to
> access a field/virtual member function of a null object.
>
> Can "Out of memory" be an Error? No, because e.g. if I read a user file
> that require me to create a large array (> 100 MiB, e.g.) I don't want
> to crash, but just tell, that "Dear user, the file can't be opened
> because it requires..."
>
>
> So what am I think can be an Error? IMHO, nothing. Because throwing
> everything can indicate that program in very good/terribly bad state and
> compiler doesn't know anything about that. And because fatal error is
> fatal the program should just try to print error and close instead of
> throwing something.
>
>
> Let's now return to the real D world. Current implementation treats
> Errors as Exceptions for now. Documentation keeps silence. All listed
> "can't be an error" cases are Errors (and it's terrible).
>
> So why do we has Exception/Error division in D? Because of nothrow.
> Personally I don't need nothrow for that high cost of making D unusable
> for me. Lets realize and solve Exception/Error problem and solve nothrow
> in the second place.
>
>
> Related links:
> http://forum.dlang.org/thread/1566418.J7qGkEti3s@lyonel
> http://d.puremagic.com/issues/show_bug.cgi?id=8135
> http://d.puremagic.com/issues/show_bug.cgi?id=8136
> http://d.puremagic.com/issues/show_bug.cgi?id=8137
>
>
> P.S.
> By the way, the only problem I see in current implementation is a luck
> of "Object finalized" assertion ("True disposable objects (add
> "Finalized!" assertion)" NG thread that didn't interest anybody).
>

The fact that error don't trigger scope and everything is nonsensial.

Today, we know how to implement exception with NO RUNTIME COST when 
exception isn't thrown. No reason not to do it, except executable size. 
As this is a specific constraint, we may want to enable it by a compiler 
switch, but not by default.

I see Error as problem that can occur anywhere in any piece of code.

I think D have some flaw in Exception management. See Segfault vs 
NullPointerException discussions in that very forum. Segfault may be OK 
for some application, but not for a server app that need to be robust.

Error exists because of nothrow. As some exceptions can be thrown 
ANYWHERE, we need a way to separate what is expected to fail, and that 
have to be handled to be nothrow, and what can go wrong anywhere 
(basically, when druntime cannot do its job for some reasons).
May 30, 2012
Re: Exception/Error division in D
Le 24/05/2012 20:39, Steven Schveighoffer a écrit :
> I'd argue this is unrecoverable. Access Violation results from
> corruption, and the damage has already been done. Even a null pointer
> access can be the cause of previous corruption. There is no "recovery"
> from memory corruption. There is no way to plan for it.
>
> Now, if you want to handle it specifically for your program, that should
> be doable, at your own risk. But there's no way throwing an Exception is
> a valid result.
>

https://github.com/D-Programming-Language/druntime/pull/187

I think this is a mandatory patch due to nullable types by default. 
Arguably, nullable by default is the problem.
« First   ‹ Prev
1 2 3 4 5
Top | Discussion index | About this forum | D home