4 ms·
Writing exception safe code is hard No it’s not. Be sure to clean up anything that could need cleaning up in a finally block. This is like saying... be sure t
by reginaldo 14y ago
Writing exception safe code is hard
No it’s not. Be sure to clean up anything that could need cleaning up in a finally block.
This is like saying... be sure to never copy more bytes than the buffer capacity. Easier said than done.
Writing exception safe code is very hard. Do not take my world for it. Read Alessandro Warth's paper (with Alan Kay as a co-author) [1]. Do not skip section 3...
Let me quote section 3.1:
In languages that support exception-handling mechanisms (e.g., the try/catch statement), a piece of code is said to be exception-safe if it guarantees not to leave the
program in an inconsistent state when an exception is thrown. Writing exception-safe
code is a tall order, as we illustrate with the following example:
try {
for (var idx = 0; idx < xs.length; idx++)
xs[idx].update();
} catch (e) {
// ...
}
Our intent is to update every element of xs, an array. The problem is that if one of
the calls to update throws an exception, some (but not all) of xs’ elements will have
been updated. So in the catch block, the program should restore xs to its previous
consistent state, in which none of its elements was updated.
One way to do this might be to make a copy of every element of the array before
entering the loop, and in the catch block, restore the successfully-updated elements to
their previous state. In general, however, this is not sufficient since update may also
have modified global variables and other objects on the heap. Writing truly exceptionsafe code is difficult and error-prone.
Now, I have seen a lot of code, and very very very few times I've seen someone restoring the state of a collection after an exception blows.
[1] http://www.vpri.org/pdf/tr2011001_final_worlds.pdf http://www.vpri.org/pdf/tr2011001_final_worlds.pdf
- btipling 14y agoHow would you write this without exception handling, how would that make it any easier? Perhaps do not put the entire loop in the try catch, but just the single iteration.
- reginaldo 14y agoOh no. I'm not saying it would be easier without exceptions. I'm just saying that the current status of our programming tools makes writing safe code very hard, and exceptions are one more thing you have to think about. I use them a lot... The authors of the paper propose their "worlds" API as a way to make writing safe code easier. They're still using exceptions, but they would require no cleanup... The code becomes: try { in thisWorld.sprout() { for (var idx = 0; idx < xs.length; idx++) xs[idx].update(); thisWorld.commit(); } } catch (e) { // no clean-up required! } So it's commit for data structures for data structures. If an exception is thrown and the commit line is not executed, no changes will be visible.
- deleted 14y ago[deleted]
- aneth4 14y agoYes, that's not exception safe. Writing this error safe without exceptions requires nearly identical discipline. How much non-exception code have you seen checking every return value and restoring state? There are alternative more safe ways of writing this, but none have to do with whether exceptions were used. This is a case where the developer needs to know that all update calls may not be completed. This may be a high or low probability, and may have fatal or no consequence. How many programs can handle a hard drive crashing or CPU glitch? Writing good error handling requires a lot more than removing exceptions, and often is not worth the cost. No software handles every error condition.
- simias 14y agoIn my opinion, this is not an issue with exception handling. It's an issue with bad programmers. You can dumb it down as much as you want, people are still going to screw it up. And you have exactly the same issue with "classical" error handling: you have to undo the partial job. Here's a bit of C code I wrote just this morning which closely ressembles your example: for (i = 0; i < pdata->overlay_nr; i++) if ((ret = overlay_init(data, i))) { while (i--) overlay_destroy(data, i); goto overlay_err; } No exceptions, but I still have to remember to clean up before I dispatch the error. I use exception quite a bit in C++, but it does fit my coding style. I use RAII almost exclusively, which alleviates most of the issues. That means that I don't have try{}catches everywhere, mostly only when I want to do actual error handling. Exceptions are a tool, it's up to you to use it correctly (or not use it).
- acdha 14y agoComplex logic isn't going to be easy no matter how you structure it because the problem in question is intrinsically hard. The real question is whether exceptions are harder or easier to write or understand than the alternatives. Your example illustrates this nicely: say you jumped on the ideological bandwagon and decided that exceptions are evil and must be avoided. How does this change your code? You can try C / golang style where errors must explicitly be checked & routed through normal flow control - something like this has a million variants floating around: var recover = 0 for (var idx = 0; idx < xs.length; idx++) err = xs[idx].update() if err: recover = 1 break } if recover > 0: // TODO: hard part goes here In either case, all you're talking about are minor semantics (i.e. whether you have to explicitly code an if statement, use a flag or goto, etc.). The actual hard problem is what meaningfully can be done for recovery and conflating that with the mechanics of how you reach that code isn't particularly interesting.
- aneth4 14y ago"ideological bandwagon" is precisely correct. It's people blaming their golf clubs for not being good enough, when the real problem is they need to learn to swing properly.
- DRMacIver 14y ago> This is like saying... be sure to never copy more bytes than the buffer capacity. Easier said than done. A solved problem except when people insist on using approaches where it's not a solved problem? I couldn't agree more! :-) firstly, the update example is a bait and switch. Partially updating an array is not inconsistent. It may be wrong depending on what the contract of the function says but that's but the same as inconsistent. Indeed the example that prompted this article back when I wrote it many moons ago was a case where you wouldn't want the state restored > In general, however, this is not sufficient since update may also have modified global variables and other objects on the heap "Doctor! It hurts when I do this!" "Well don't do that then" It's hard to write any correct code when you're mutating globals and arguments all over the place. Exceptions can certainly make this worse. In general if you're not using globals or mutating your arguments this isn't a hard problem. If you are mutating arguments and you want to ensure a "restore all state if an exception is thrown behaviour then yes this is sometimes hard (unless your argument supports a rollback mechanism - e.g. it's a database) but I submit that a large part of why you haven't seen that is that it's a contract people don't care enough about to support