October 23, 2008
Sean Kelly wrote:
> Jarrett Billingsley wrote:
>> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>>> Errors represent situations which are typically non-recoverable--program
>>> logic errors, for example, or situations where data corruption may have
>>> occurred--while Exceptions represent the bulk of normal execution errors,
>>> including OutOfMemory conditions.
>>
>> How, pray tell, is an app supposed to recover from an out-of-memory condition?
> 
> By releasing dynamically allocated memory.  I'd expect some to be released automatically as the stack is unrolled to the catch point anyway.  For example:
> 
> void main()
> {
>     try { fn(); }
>     catch( Exception e ) {}
>     int[] x = new int[16384];
> }
> 
> void fn()
> {
>     int[] x = new int[16384];
>     fn();
> }
> 
> Eventually this app will run out of memory (hopefully before it runs out of stack space) and an OutOfMemoryException will be thrown.  As the stack is unwound, all valid references to this memory will be released.  So the allocation in main() should trigger a collection which frees up all the now-unreferenced memory, thus allowing the allocation in main() to succeed.
> 
> For manual recovery, consider an app that does a great deal of internal caching.  On an OutOfMemory condition the app could clear its caches and  then retry the operation.  This is probably a bad example, but I think the general idea of trapping and recovering from such a state is potentially valid.
> 
> 
> Sean

Didn't see this discussion before I went off my tirade. I agree it's recoverable and in a perfect world this would be so, but look through any large codebase for how many catch(Exception) blocks there are. I'll bet you that NONE of the general catch(Exception) blocks (except the ones that print an error and exit the program) expect to see, or are prepared for, an out of memory exception.

Asking programmers to think about out of memory errors is too much. We're trained to assume computers have infinite memory and when they run out, the system/runtime is supposed to do drastic things like crashing our programs - not start introducing strange logic errors all over the place because programmers didn't realize their catch(Exception) blocks had to deal with more than "file doesn't exist"
October 23, 2008
Andrei Alexandrescu wrote:
> Jarrett Billingsley wrote:
>> On Wed, Oct 22, 2008 at 6:49 AM, Jacob Carlborg <doobnet@gmail.com> wrote:
>>> I think the name ArrayBoundsException should be changed to a more general
>>> name like BoundsException, OutOfBoundsException or
>>> IndexOutOfBoundsException. Then you can use the exception in every class
>>> that have some sort of index operation and not just for an array/array
>>> class.
>>>
>>
>> 2nded.
> 
> I agree. In fact I wanted to ask you all the following question. What do
> you think about the current exception hierarchy in phobos? I think it is
> terrible. Each module in std you open, the first piece of code to be
> seen is the "class ThisModuleNameException" definition. In many (most?)
> cases the module-specific exception does absolutely nothing in addition
> to its base class. The putative reader (including me) tends to scroll non-critically over that passage without even blinking, mumbling in a trance - of course, yes, each module should define at least one exception type.
> 
> Until one day when you stop scrolling and say, wait a minute. This all is repetition. And there are alternatives to catching by type - you can catch the base type and consult a field. And in fact I don't remember seeing code that depends on exceptions thrown from different modules having different types. There's something wrong here!
> 
> I think most exception classes in phobos should be yanked if it's possible for their functionality (often nil) to be moved in the Exception base class. The module name should be a member. If someone needs to deal with an exception thrown from a specific module, they can always inspect the field. We don't need a huge hierarchy for that.
> 
> 
> Andrei

Yes, you _could_ use a field... but the "catch a subclass" style is already there and is supported by the language, so _why_ use a field? Which of the following is easier?:

Option A:
---------
try
{
     new Socket(30587);
}
catch(SocketException e)
{
     printf("Could not open socket\n");
}

Option B:
---------
try
{
    new Socket(30587);
}
catch(Exception e)
{
    if(e.type == ExceptionType.Socket)
        printf("Could not open socket\n");
    else
        throw e;
}
October 23, 2008
Robert Fraser wrote:
> Sean Kelly wrote:
>> Denis Koroskin wrote:
>>> On Wed, 22 Oct 2008 16:22:02 +0400, Jarrett Billingsley <jarrett.billingsley@gmail.com> wrote:
>>>
>>>> On Wed, Oct 22, 2008 at 6:49 AM, Jacob Carlborg <doobnet@gmail.com> wrote:
>>>>> I think the name ArrayBoundsException should be changed to a more general
>>>>> name like BoundsException, OutOfBoundsException or
>>>>> IndexOutOfBoundsException. Then you can use the exception in every class
>>>>> that have some sort of index operation and not just for an array/array
>>>>> class.
>>>>>
>>>>
>>>> 2nded.
>>>
>>> Agreed. BTW, why it is not an Error but Exception?
>>
>> The Error class was created shortly before release and I didn't get around to reclassifying the exceptions in druntime.  From memory though, I think that OutOfMemoryException and UtfException should remain as-is, but the remaining exceptions should probably be errors.  Any objections?
>>
>> And as for ArrayBoundsException... how about IndexOutOfBoundsError. It's long, but probably the most self-explanatory.  Also, any value in retaining ArrayBoundsError and having it subclass this?  I'd think not, but figured I'd ask anyway.
>>
>>
>> Sean
> 
> Ah NOOOOO! Please don't make an out of memory catchable with a plain catch(Exception) .... In most programs... pretty much every non-trivial desktop application I can think of... an out-of memory state is basically unrecoverable. In the few cases it would need to be, there would need to be significant handling code, and that would have to be at the right location in the program (i.e. when first opening a file), not in the closest catch-all block some intern down the hallway may have thrown in.
> 
> Out of memory is an Error subclass in Java and C#, so there's precedent there. catch(Exception) shouldn't be used - but it is, quite often, so things that would cause issues that a user could not normally be expected to recover from should be Error subclasses.
> 
> Indexing I could see being an exception since that's deterministic and the user's fault. So I guess I disagree on both counts ;-P. Although since index errors don't appear in release modes in D, that gives more case for it to be an Error.

Make OutOfMemoryException the own subclass of Throwable? Then we have

Throwable
  Exception
  Error
  OutOfMemoryException

or we maybe we can have (jokingly)

Throwable
  Exception    // Recoverable
  Error        // Should not be recoverable
  ChallengingException  // Recoverable but I won't touch it.
    OutOfMemoryException // Let programmer to force GC to release memory
    CPUOnFireException   // Let programmer to call local fire station through modem.
    // etc...

October 23, 2008
Wed, 22 Oct 2008 19:18:57 -0400,
Jarrett Billingsley wrote:
> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
> > Errors represent situations which are typically non-recoverable--program logic errors, for example, or situations where data corruption may have occurred--while Exceptions represent the bulk of normal execution errors, including OutOfMemory conditions.
> 
> How, pray tell, is an app supposed to recover from an out-of-memory condition?

In an image editor, a user asks to create a huge image (50k x 50k).  You try, run out of memory, and gracefully tell the user that the image was too big and you didn't succeed.
October 23, 2008
Wed, 22 Oct 2008 12:01:36 -0700,
Sean Kelly wrote:
> And as for ArrayBoundsException... how about IndexOutOfBoundsError. It's long, but probably the most self-explanatory.  Also, any value in retaining ArrayBoundsError and having it subclass this?  I'd think not, but figured I'd ask anyway.

Maybe IndexingError ?  There are not necessarily any bounds when you're indexing into an arbitrary collection.
October 23, 2008
On Thu, 23 Oct 2008 04:02:22 +0400, Sean Kelly <sean@invisibleduck.org> wrote:

> Jarrett Billingsley wrote:
>> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>>> Errors represent situations which are typically non-recoverable--program
>>> logic errors, for example, or situations where data corruption may have
>>> occurred--while Exceptions represent the bulk of normal execution errors,
>>> including OutOfMemory conditions.
>>  How, pray tell, is an app supposed to recover from an out-of-memory condition?
>
> By releasing dynamically allocated memory.  I'd expect some to be released automatically as the stack is unrolled to the catch point anyway.  For example:
>
> void main()
> {
>      try { fn(); }
>      catch( Exception e ) {}
>      int[] x = new int[16384];
> }
>
> void fn()
> {
>      int[] x = new int[16384];
>      fn();
> }
>
> Eventually this app will run out of memory (hopefully before it runs out of stack space) and an OutOfMemoryException will be thrown.  As the stack is unwound, all valid references to this memory will be released.   So the allocation in main() should trigger a collection which frees up all the now-unreferenced memory, thus allowing the allocation in main() to succeed.
>
> For manual recovery, consider an app that does a great deal of internal caching.  On an OutOfMemory condition the app could clear its caches and   then retry the operation.  This is probably a bad example, but I think the general idea of trapping and recovering from such a state is potentially valid.
>
>
> Sean

I think that OutOfMemoryException should *not* be recoverable. Instead, language should provide some hookable callback (like onOutOfMemoryError()) which is called when memory limit is reached so that program may free some unused memory (which is held by user since it is not garbage-collected) and tries to allocate the memory again without failure (return true from callback). User might decide to re-throw some other kind of *exception*, like NotEnoughMemoryException(), to catch and recover, or pass it (return false from callback) which in turn will finally throw OutOfMemoryError().
October 23, 2008
Robert Fraser wrote:
> Andrei Alexandrescu wrote:
>> Jarrett Billingsley wrote:
>>> On Wed, Oct 22, 2008 at 6:49 AM, Jacob Carlborg <doobnet@gmail.com> wrote:
>>>> I think the name ArrayBoundsException should be changed to a more general
>>>> name like BoundsException, OutOfBoundsException or
>>>> IndexOutOfBoundsException. Then you can use the exception in every class
>>>> that have some sort of index operation and not just for an array/array
>>>> class.
>>>>
>>>
>>> 2nded.
>>
>> I agree. In fact I wanted to ask you all the following question. What do
>> you think about the current exception hierarchy in phobos? I think it is
>> terrible. Each module in std you open, the first piece of code to be
>> seen is the "class ThisModuleNameException" definition. In many (most?)
>> cases the module-specific exception does absolutely nothing in addition
>> to its base class. The putative reader (including me) tends to scroll non-critically over that passage without even blinking, mumbling in a trance - of course, yes, each module should define at least one exception type.
>>
>> Until one day when you stop scrolling and say, wait a minute. This all is repetition. And there are alternatives to catching by type - you can catch the base type and consult a field. And in fact I don't remember seeing code that depends on exceptions thrown from different modules having different types. There's something wrong here!
>>
>> I think most exception classes in phobos should be yanked if it's possible for their functionality (often nil) to be moved in the Exception base class. The module name should be a member. If someone needs to deal with an exception thrown from a specific module, they can always inspect the field. We don't need a huge hierarchy for that.
>>
>>
>> Andrei
> 
> Yes, you _could_ use a field... but the "catch a subclass" style is already there and is supported by the language, so _why_ use a field? Which of the following is easier?:
> 
> Option A:
> ---------
> try
> {
>      new Socket(30587);
> }
> catch(SocketException e)
> {
>      printf("Could not open socket\n");
> }
> 
> Option B:
> ---------
> try
> {
>     new Socket(30587);
> }
> catch(Exception e)
> {
>     if(e.type == ExceptionType.Socket)
>         printf("Could not open socket\n");
>     else
>         throw e;
> }

I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.

That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.


Andrei
October 23, 2008
Sergey Gromov wrote:
> Wed, 22 Oct 2008 19:18:57 -0400,
> Jarrett Billingsley wrote:
>> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>>> Errors represent situations which are typically non-recoverable--program
>>> logic errors, for example, or situations where data corruption may have
>>> occurred--while Exceptions represent the bulk of normal execution errors,
>>> including OutOfMemory conditions.
>> How, pray tell, is an app supposed to recover from an out-of-memory condition?
> 
> In an image editor, a user asks to create a huge image (50k x 50k).  You try, run out of memory, and gracefully tell the user that the image was too big and you didn't succeed.

I could extract many instances of this pattern from my programs. Unfortunately, current bugs in the compiler, phobos, or both lead to sudden death of the application under certain circumstances. I hope one day I'll get around to investigating that.

Andrei
October 23, 2008
Sergey Gromov wrote:
> Wed, 22 Oct 2008 12:01:36 -0700,
> Sean Kelly wrote:
>> And as for ArrayBoundsException... how about IndexOutOfBoundsError. It's long, but probably the most self-explanatory.  Also, any value in retaining ArrayBoundsError and having it subclass this?  I'd think not, but figured I'd ask anyway.
> 
> Maybe IndexingError ?  There are not necessarily any bounds when you're indexing into an arbitrary collection.

STL has range_error. I like that because it's rather general.

Andrei
October 23, 2008
Denis Koroskin wrote:
> On Thu, 23 Oct 2008 04:02:22 +0400, Sean Kelly <sean@invisibleduck.org> wrote:
> 
>> Jarrett Billingsley wrote:
>>> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>>>> Errors represent situations which are typically non-recoverable--program
>>>> logic errors, for example, or situations where data corruption may have
>>>> occurred--while Exceptions represent the bulk of normal execution errors,
>>>> including OutOfMemory conditions.
>>>  How, pray tell, is an app supposed to recover from an out-of-memory condition?
>>
>> By releasing dynamically allocated memory.  I'd expect some to be released automatically as the stack is unrolled to the catch point anyway.  For example:
>>
>> void main()
>> {
>>      try { fn(); }
>>      catch( Exception e ) {}
>>      int[] x = new int[16384];
>> }
>>
>> void fn()
>> {
>>      int[] x = new int[16384];
>>      fn();
>> }
>>
>> Eventually this app will run out of memory (hopefully before it runs out of stack space) and an OutOfMemoryException will be thrown.  As the stack is unwound, all valid references to this memory will be released.   So the allocation in main() should trigger a collection which frees up all the now-unreferenced memory, thus allowing the allocation in main() to succeed.
>>
>> For manual recovery, consider an app that does a great deal of internal caching.  On an OutOfMemory condition the app could clear its caches and   then retry the operation.  This is probably a bad example, but I think the general idea of trapping and recovering from such a state is potentially valid.
>>
>>
>> Sean
> 
> I think that OutOfMemoryException should *not* be recoverable. Instead, language should provide some hookable callback (like onOutOfMemoryError()) which is called when memory limit is reached so that program may free some unused memory (which is held by user since it is not garbage-collected) and tries to allocate the memory again without failure (return true from callback). User might decide to re-throw some other kind of *exception*, like NotEnoughMemoryException(), to catch and recover, or pass it (return false from callback) which in turn will finally throw OutOfMemoryError().

But one of the best things possible to do is unwind the stack and fall back to a higher position with less state. A function cannot do that, an exception can. Why do you guys want to avoid exceptions in one of the few cases when they are exactly, but exactly what the doctor prescribed?

Andrei