Author Topic: Eric Lippert - Vexing Exceptions  (Read 221 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4566
Eric Lippert - Vexing Exceptions
« on: October 11, 2018, 01:22:43 AM »
This had an interesting little summary of types of exceptions:
Eric Lippert - Vexing Exceptions

The types were:
  • fatal
  • boneheaded
  • vexing
  • exogenous

Basically, don't catch fatal exceptions (disk failure, access violation), there is nothing you can really do anyway. Don't catch boneheaded exceptions (array index out of bounds), as they reveal a bug in your code, and so you should fix the bug, rather than try to catch and silence the exception. You're forced to catch vexing exceptions (Int32.Parse()), though these are really a misuse of exceptions and give them a bad name. Finally, do catch and deal with exogenous exceptions (file not found, permission denied), which seem to be at the heart of what checked exceptions are about.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2040
    • LairWorks Entertainment
Re: Eric Lippert - Vexing Exceptions
« Reply #1 on: October 12, 2018, 10:08:53 PM »
This is an interesting article but there are some things that could be disagreed with. For instance, the Out of Memory exception. This is not always a fatal exception. There was a case that I would catch it in a Map Editor that allowed the user to decide whatever size map they wanted. If they put in absurd sizes like 50,000,000 x 50,000,000, inevitably this would result in an out of memory exception being thrown... but this is a case where the program doesn't need to barf. Since the allocation fails, the memory is ultimately freed. The editor could catch the exception, yell at the user, and continue merrily along. It can be argued that this is an edge case but I don't think out of memory exceptions really need to be treated as "OMG FAIL NOW!".

I'd also argue that not catching exceptions leads to programs that 'crash' and it's not pretty for users. If you come across an exception that can't be recovered from for whatever reason, as the developer you should do your best to fail gracefully. E.g., do something to let the user know why they're suddenly getting kicked back out to the desktop, log it in some way and then terminate.

There are probably fifty thousand points of contention here and so many different ways of handling it and so many different ways of thinking about it that I'm not even sure it's a productive discussion. I do wish, though, that it didn't feel like a religious argument. Some developers get so bent out of shape over exception handling and what they consider to be the holy grail of exception handling.
- Leeor
LairWorks Entertainment

Titanum UFO's

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4566
Re: Eric Lippert - Vexing Exceptions
« Reply #2 on: October 13, 2018, 04:20:12 AM »
I agree wholeheartedly on your Out of Memory Exception point. There are plenty of cases where you should catch and handle that. Particularly in a robust end user system, and particularly in the case you described. I thought the author gave kind of a bad example for that one.

The main concern for the case you described is where you should catch that exception, not if you should catch it. I'd say don't catch an out of memory exception within an initialization routine, or within a load/parse routine for map data, but rather around the initialization or load/parse so the operation fails as a whole.

I should also point out, that in most cases, you probably wouldn't catch the out of memory exception specifically, but rather catch any runtime exception, which might be an out of memory exception, or perhaps one of the other many ways a component could fail to do what was asked. Often the important point is not why something failed, but rather simply that it failed.

If you catch an exception specifically, then presumably there is some unique way you can respond to that type of exception. Perhaps use a slower but more memory efficient algorithm when you encounter an out of memory exception.


With that said, there are many simple command line utility programs that have only a single purpose. If there is nothing they can do because the problem set is too big to fit into memory, than yeah, they should just let the exception bubble up, and have it reported to the user and terminate. I think maybe this is the case the author was thinking of. It's kind of a special case though.

There is often no point to catch an out of memory exception in small utility programs, and there is often little point to check specifically for out of memory exceptions in larger programs, though larger programs should check generally for runtime exceptions, which include out of memory.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2040
    • LairWorks Entertainment
Re: Eric Lippert - Vexing Exceptions
« Reply #3 on: October 13, 2018, 11:27:13 AM »
The main concern for the case you described is where you should catch that exception, not if you should catch it. I'd say don't catch an out of memory exception within an initialization routine, or within a load/parse routine for map data, but rather around the initialization or load/parse so the operation fails as a whole.

This is what I mean. The code in question looked a lot like this:

Code: [Select]
size_t absurdSize = 0xFFFFFFFF; /* This would usually come from user
                                   input, not declared here like this */
try
{
    new Map(absurdSize, absurdSize);
}
catch(std::bad_alloc& egad)
{
    /* This is definitely not a fatal exception, the
       program can easily continue normally here. */
    std::cout << "Map size too big. Use reasonable dimensions." << std::endl;
}

/* note lack of trying to catch anything else because reasons. */
- Leeor
LairWorks Entertainment

Titanum UFO's

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 712
Re: Eric Lippert - Vexing Exceptions
« Reply #4 on: October 13, 2018, 11:46:54 AM »
That kind of error leeor, could be solved with a bit of "if" logic, and thus wouldn't be as costly for the program to process ( at least that is what I've been told about catching exceptions ). For example:

Code: [Select]
if absurdsize > 1024:
   absurdsize = 1024

Then if the concern was that they might give a value that would overflow (not sure right term) the integer, you would clamp the size of the input box down so that only a value of say 9999 could be put in it.

Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2040
    • LairWorks Entertainment
Re: Eric Lippert - Vexing Exceptions
« Reply #5 on: October 13, 2018, 12:06:25 PM »
... you've entirely missed the point of what we're addressing here.

We're talking about recovering from certain types of exceptions, not how to sanitize user inputs. That is a whole different topic and not a simple one to boot.

We're also not talking about integer overflows -- that's an entirely different problem. We're talking about failed memory allocation (aka bad_alloc exceptions in C++) and when, where and if to address them.
« Last Edit: October 13, 2018, 12:13:57 PM by leeor_net »
- Leeor
LairWorks Entertainment

Titanum UFO's

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4566
Re: Eric Lippert - Vexing Exceptions
« Reply #6 on: October 13, 2018, 08:33:33 PM »
For reference, here is some documentation on the exception class, which shows the hierarchy.

To my surprise, bad_alloc derives directly from exception, and not from runtime_error. A catch clause for runtime_error will not catch a bad_alloc exception.

In many cases, I would recommend against catching all exception objects, since some of them aren't really designed to be recovered from. I suppose you could do that when dealing with an isolated component, such as an extension module, but then the issue of exception safety comes up, and what happens if the failure corrupted your program state. Hence why not all exceptions should be caught.

Well, at the very least, given the exception hierarchy, if you want to recover from an out of memory error, it seems you do need to catch that exception specifically. My bad.



On a related note, in regards to corrupting program state. I believe the C++ exception handling mechanism may be required to allocate memory in some cases. That poses a potential problem in low memory situations. Trying to throw an exception could potentially result in an additional exception due to the low memory. I believe many implementations have a special buffer set aside for bad_alloc in these cases, though such a system might not be entirely robust. I've read somewhere there are ways to keep exceptions alive, or chain them, and it seems the memory use for exceptions is potentially unbounded. I've read some implementations can call terminate from within exception handling code if any of these allocations fail.

This might be why many people consider out of memory to be a fatal errors. If the exception handling system can't cope, you might be crashing anyway, whether you've tried to handle the error or not.

Though, those concerns are more when a small objects fails to be allocated. If you don't have 8 bytes or so left to store the exception, there's not much you can do. If however a large allocation failed, it might simply be the request was unreasonably large, and you do still have plenty of memory left. You might even have more memory left than was request, but it simply isn't contiguous. In short, a bad_alloc is not the same as being out of memory, though being out of memory can result in a bad_alloc. Assuming of course you still have the memory left for the bad_alloc. ;)

This doesn't instill much confidence in exceptions.  :(

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2040
    • LairWorks Entertainment
Re: Eric Lippert - Vexing Exceptions
« Reply #7 on: October 13, 2018, 10:44:03 PM »
It's an inherently flawed system and a system that is very often abused in horrible ways. Generally I catch exceptions only to be able to display a message related to the exception that was raised and exit gracefully. If it's a catastrophic error/exception there's no graceful crashing, just barfing with the system likely coming to its knees.
- Leeor
LairWorks Entertainment

Titanum UFO's

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 712
Re: Eric Lippert - Vexing Exceptions
« Reply #8 on: October 14, 2018, 01:44:16 AM »
Exceptions are failsafes. The are not to be used as a crutch for bad code. If you know something could cause a fatal error, you take steps to make the code able to avert disaster. Preventative coding, rather than reactive coding.

If for example you know that an error will occur for when a file can't be loaded, then you either provide an alternative or you make sure the file in some form will be loadable, such as with a backup save, in case the main save gets corrupted.

Use exceptions properly, and they will be a useful tool. Just like with every other piece of logic; use it properly.
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4566
Re: Eric Lippert - Vexing Exceptions
« Reply #9 on: October 14, 2018, 02:33:07 AM »
You can't code your way around a PowerCordRemovedException ;)



I forgot to mention another problem, relevant to Linux. The Linux kernel by default allows "overcommit":
What is Overcommit? And why is it bad?

Basically, memory allocations might succeed even though you don't have the physical memory to back it. That allows programs to allocate more memory than they need, and only fail if they try to actually use more than is available. In that case you get a memory access violation upon accessing memory which the paging system can't find a page for. This can trigger the Out Of Memory Killer (OOM-Killer), which will use some heuristic to kill a not so important process which is hogging memory, and thus free up some space.

In such a system, don't expect to be able to catch a bad_alloc exception. There won't be one at the point of allocation, as it appears the allocation succeeded. The problem comes later when using the returned pointer.

Now that really shakes my faith in relying on being able to catch out of memory exceptions.

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 712
Re: Eric Lippert - Vexing Exceptions
« Reply #10 on: October 14, 2018, 09:37:47 AM »
You keep mentioning a lack of memory from RAM... but what about the old-school ways of getting around a lack of RAM back in the days when you had only 1 megabyte of available RAM (ie the 386).

Things like XMS, EMS, or Virtual Memory.
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4566
Re: Eric Lippert - Vexing Exceptions
« Reply #11 on: October 14, 2018, 11:23:26 AM »
Yes, virtual memory. It's still relevant, though thankfully the increase in memory sizes means modern systems do a whole lot less paging to/from disk. Virtual memory is very much what overcommit is about, in that you allocate sections of the virtual address space, but don't necessarily have the physical memory, nor the swap space to back it.


As a side note, page faults and memory access violations are processor level "exceptions". These are different from C++ language level exceptions. The processor level exceptions don't generally map to language level exceptions. For page faults, the virtual memory system handles it by swapping pages around to/from disk, and the program never even knows about it. For an access violation, an OS handler generally kicks in and terminates the program. If you're getting access violations, there's a good chance program memory is already corrupted, so it may not be safe to try and recover from it.


Language level exceptions are more about keeping the main flow of logic clean of messy error handling code. Combined with things like the RAII (Resource Acquisition Is Initialization) idiom, they also help ensure proper cleanup when errors happen, so the program doesn't leak memory or other resources. It does this through the exception handling mechanism that ensures necessary destructors are called, and does all the tracking and bookkeeping for you behind the scenes.
« Last Edit: October 14, 2018, 11:25:50 AM by Hooman »