March 09, 2012 [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
The issue of catching Errors came up in D.learn today, and I need some clarification. It has been my understanding that anything which is thrown which is not derived from Exception skips all destructors, scope statements, and finally blocks, meaning that it's generally unsafe to catch them, because your program is potentially in an invalid state. However, it was brought to my attention that the compiler does not currently follow this behavior - all 3 of those get run for Errors at present. So, the question is whether I'm just completely misunderstanding something or whether something has changed.
Is it guaranteed that all thrown Throwables will result in all destructors, scope statements (exit and failure at least), and finally blocks that they pass being executed? Or is it only guaranteed for Exception and its derived types and just so happens to work for other exception types right now?
I've been completely certain that such was _not_ guaranteed unless the exception type is Exception or derived from Exception, but I can't find any support for that searching the docs or newsgroup save for what I've said about it myself. So, I'd like official clarification on the matter.
- Jonathan M Davs
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals
|
March 09, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 3/9/2012 6:29 PM, Jonathan M Davis wrote: > The issue of catching Errors came up in D.learn today, and I need some > clarification. It has been my understanding that anything which is thrown which > is not derived from Exception skips all destructors, scope statements, and > finally blocks, meaning that it's generally unsafe to catch them, because your > program is potentially in an invalid state. However, it was brought to my > attention that the compiler does not currently follow this behavior - all 3 of > those get run for Errors at present. So, the question is whether I'm just > completely misunderstanding something or whether something has changed. > > Is it guaranteed that all thrown Throwables will result in all destructors, > scope statements (exit and failure at least), and finally blocks that they pass > being executed? Or is it only guaranteed for Exception and its derived types > and just so happens to work for other exception types right now? It just so happens to work. It's wrong. > I've been completely certain that such was _not_ guaranteed unless the > exception type is Exception or derived from Exception, but I can't find any > support for that searching the docs or newsgroup save for what I've said about > it myself. So, I'd like official clarification on the matter. > _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Mar 9, 2012, at 9:38 PM, Walter Bright wrote: > > On 3/9/2012 6:29 PM, Jonathan M Davis wrote: >> The issue of catching Errors came up in D.learn today, and I need some clarification. It has been my understanding that anything which is thrown which is not derived from Exception skips all destructors, scope statements, and finally blocks, meaning that it's generally unsafe to catch them, because your program is potentially in an invalid state. However, it was brought to my attention that the compiler does not currently follow this behavior - all 3 of those get run for Errors at present. So, the question is whether I'm just completely misunderstanding something or whether something has changed. >> >> Is it guaranteed that all thrown Throwables will result in all destructors, scope statements (exit and failure at least), and finally blocks that they pass being executed? Or is it only guaranteed for Exception and its derived types and just so happens to work for other exception types right now? > > It just so happens to work. It's wrong. I'm on the fence about whether attempting cleanup when an Error is thrown is desired behavior. If there is no cleanup, why allow Errors to be caught at all? We may as well simply call abort() at the point they're thrown. _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | It would be hard to write a framework around D unit tests, for instance, where you need to catch AssertErrors if things weren't cleaned up properly (or if it just called abort()). That's just the first thing that comes to mind. Regards, Alex On Mon, Mar 12, 2012 at 8:34 PM, Sean Kelly <sean@invisibleduck.org> wrote: > On Mar 9, 2012, at 9:38 PM, Walter Bright wrote: >> >> On 3/9/2012 6:29 PM, Jonathan M Davis wrote: >>> The issue of catching Errors came up in D.learn today, and I need some clarification. It has been my understanding that anything which is thrown which is not derived from Exception skips all destructors, scope statements, and finally blocks, meaning that it's generally unsafe to catch them, because your program is potentially in an invalid state. However, it was brought to my attention that the compiler does not currently follow this behavior - all 3 of those get run for Errors at present. So, the question is whether I'm just completely misunderstanding something or whether something has changed. >>> >>> Is it guaranteed that all thrown Throwables will result in all destructors, scope statements (exit and failure at least), and finally blocks that they pass being executed? Or is it only guaranteed for Exception and its derived types and just so happens to work for other exception types right now? >> >> It just so happens to work. It's wrong. > > I'm on the fence about whether attempting cleanup when an Error is thrown is desired behavior. If there is no cleanup, why allow Errors to be caught at all? We may as well simply call abort() at the point they're thrown. > _______________________________________________ > dmd-internals mailing list > dmd-internals@puremagic.com > http://lists.puremagic.com/mailman/listinfo/dmd-internals _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Alex Attachments:
| On 12 mar 2012, at 21:06, Alex wrote: > It would be hard to write a framework around D unit tests, for instance, where you need to catch AssertErrors if things weren't cleaned up properly (or if it just called abort()). That's just the first thing that comes to mind. > > Regards, > Alex > I completely agree. That's what I've been discussion in the "learn" newsgroup. I had a look through the source code of druntime and found "onAssertError". I don't know if it's used or not but sounds exactly like the function I need. But unfortunately the function to set the assert handler is deprecated. http://dlang.org/phobos/core_exception.html#onAssertError http://dlang.org/phobos/core_exception.html#setAssertHandler -- /Jacob Carlborg |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 12 March 2012 20:34, Sean Kelly <sean@invisibleduck.org> wrote: > On Mar 9, 2012, at 9:38 PM, Walter Bright wrote: >> >> On 3/9/2012 6:29 PM, Jonathan M Davis wrote: >>> The issue of catching Errors came up in D.learn today, and I need some clarification. It has been my understanding that anything which is thrown which is not derived from Exception skips all destructors, scope statements, and finally blocks, meaning that it's generally unsafe to catch them, because your program is potentially in an invalid state. However, it was brought to my attention that the compiler does not currently follow this behavior - all 3 of those get run for Errors at present. So, the question is whether I'm just completely misunderstanding something or whether something has changed. >>> >>> Is it guaranteed that all thrown Throwables will result in all destructors, scope statements (exit and failure at least), and finally blocks that they pass being executed? Or is it only guaranteed for Exception and its derived types and just so happens to work for other exception types right now? >> >> It just so happens to work. It's wrong. > > I'm on the fence about whether attempting cleanup when an Error is thrown is desired behavior. If there is no cleanup, why allow Errors to be caught at all? We may as well simply call abort() at the point they're thrown. I went to a lot of trouble to ensure the exception chaining behaviour of Errors vs Exceptions. Personally, I just don't buy the argument that the entire system is in an invalid state when an Error occurs. It's a sign that some subsystem is in a invalid state, but how much of the whole app is invalid, depends on the particular situation. For example, if you're extensively using class invariants and contracts, and you get an AssertError, then the possible damage is pretty small. Especially if it is an assert failure in a pure @safe function; cleanup will be perfectly safe in that situation. I would have thought that if you had a truly fatal error, you would have called exit() or something, instead of throwing an Error. _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Don Clugston | On Monday, March 12, 2012 21:29:25 Don Clugston wrote: > I went to a lot of trouble to ensure the exception chaining behaviour of Errors vs Exceptions. > > Personally, I just don't buy the argument that the entire system is in an invalid state when an Error occurs. It's a sign that some subsystem is in a invalid state, but how much of the whole app is invalid, depends on the particular situation. For example, if you're extensively using class invariants and contracts, and you get an AssertError, then the possible damage is pretty small. Especially if it is an assert failure in a pure @safe function; cleanup will be perfectly safe in that situation. > > I would have thought that if you had a truly fatal error, you would have called exit() or something, instead of throwing an Error. When an Error is thrown, the program is in a potentially invalid state. How invalid it is, depends on the Error. And the fact that cleanup isn't guaranteed _definitely_ puts the program in an invalid state when an Error is thrown unless it's caught very close to its throw point. I'm not sure how much it matters whether there is any attempt to cleanup from Errors except that if the Error _does_ indicate something which would invalidate the cleanup code, it could cause serious problems. The main one that I can think of is OutOfMemoryError, since if any cleanup code then tries to allocate anything, it's going to fail. Of course, that would then result in _another_ OutOfMemoryError, so it may be that that pretty much takes care of itself and really isn't a big deal. Certainly, in the general case, even if cleanup _is_ guaranteed, catching Errors is a bad idea because of the types of problems that they generally indicate. But there _are_ cases where catching Errors can be useful - the most prevalent one being catching AssertErrors in unit tests. Anyone trying to use a fancier unit testing framework (which I don't personally see any need for, but some people are _very_ interested in it - e.g. Jacob Carlborg) is going to have to do that. And in rare cases, even catching OutOfMemoryError makes some sense. You just have to know what you're doing and be careful. The _biggest_ thing with Errors is simply that they're not Exceptions, so catch(Exception e) {} won't catch them, and nothrow isn't affected by them. That right there provides their primary benefit. Personally, I'm on the fence as to whether attempting cleanup when an Error is thrown is a good idea or not. Certainly, in the unit testing case, it would certainly be desirable. Most of the rest, it probably doesn't matter all that much, but it would arguably be desirable to skip cleanup for them so that the amount of weird stuff that can happen on shutting down the program can be minimized. But of course, it also makes it harder to guarantee that certain things are done on shutdown, and it's not all that uncommon to have stuff that you want to _guarantee_ runs on shutdown, even if it's not a clean shutdown (or even _especially_ if it's not a clean shutdown). The other thing to consider is the fact that very few people seem to understand that Errors in flight aren't guaranteed to execute scope statements, destructors, or finally blocks. So, there's definitely code being written which relies on behavior that is not guaranteed. The fact that it currently happens in most (all?) cases just makes it worse, since then there's more stuff that will break (particularly unit testing frameworks like Jacob's) when it does. Heck, simply being able to rely on scope(failure) running for AssertErrors would be valuable for unit testing, since it makes it much easier to output extra information on failures. And I expect that _that_ is done quite frequently. I'm pretty sure that I've done it a number of times myself, forgetting that that's not guaranteed to work. So, there's definitely code out there which relies on Errors triggering all of the appropriate cleanup stuff just like Exceptions do. I'd argue that we should decide whether cleanup on Errors should be guaranteed or not and then make it so that the compiler _always_ follows this behavior so that the behavior - whatever it may be - _is_ guaranteed. Having the behavior be undefined is just causing problems. - Jonathan M Davis _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Mar 12, 2012, at 1:13 PM, Jacob Carlborg wrote: > On 12 mar 2012, at 21:06, Alex wrote: > >> It would be hard to write a framework around D unit tests, for instance, where you need to catch AssertErrors if things weren't cleaned up properly (or if it just called abort()). That's just the first thing that comes to mind. > > I completely agree. That's what I've been discussion in the "learn" newsgroup. I had a look through the source code of druntime and found "onAssertError". I don't know if it's used or not but sounds exactly like the function I need. But unfortunately the function to set the assert handler is deprecated. > > http://dlang.org/phobos/core_exception.html#onAssertError http://dlang.org/phobos/core_exception.html#setAssertHandler The handler is deprecated because DMD doesn't generate a valid call stack for _d_assert (which calls onAssertError) so it's impossible to return from the assert handler without throwing. This rendered the assert handler largely useless and after a few years of no one using it I decided to deprecate it. I'd be happy to keep the assert handler if people actually want it though, or if DMD changes its codegen. Personally, I'd like to be able to assert without throwing in some testing situations, and overriding a handler seems like an appropriate way to do this. _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 3/12/2012 12:34 PM, Sean Kelly wrote: > > I'm on the fence about whether attempting cleanup when an Error is thrown is desired behavior. If there is no cleanup, why allow Errors to be caught at all? We may as well simply call abort() at the point they're thrown. > So that an informative message can be printed, the backup engaged, attempt to shut down gracefully, log the failure details to a file, etc. _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
March 12, 2012 Re: [dmd-internals] Throwing Errors | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 12 March 2012 22:06, Jonathan M Davis <jmdavisProg@gmx.com> wrote: > On Monday, March 12, 2012 21:29:25 Don Clugston wrote: >> I went to a lot of trouble to ensure the exception chaining behaviour of Errors vs Exceptions. >> >> Personally, I just don't buy the argument that the entire system is in an invalid state when an Error occurs. It's a sign that some subsystem is in a invalid state, but how much of the whole app is invalid, depends on the particular situation. For example, if you're extensively using class invariants and contracts, and you get an AssertError, then the possible damage is pretty small. Especially if it is an assert failure in a pure @safe function; cleanup will be perfectly safe in that situation. >> >> I would have thought that if you had a truly fatal error, you would have called exit() or something, instead of throwing an Error. > > When an Error is thrown, the program is in a potentially invalid state. How invalid it is, depends on the Error. And the fact that cleanup isn't guaranteed _definitely_ puts the program in an invalid state when an Error is thrown unless it's caught very close to its throw point. > > I'm not sure how much it matters whether there is any attempt to cleanup from Errors except that if the Error _does_ indicate something which would invalidate the cleanup code, it could cause serious problems. The main one that I can think of is OutOfMemoryError, since if any cleanup code then tries to allocate anything, it's going to fail. Of course, that would then result in _another_ OutOfMemoryError, so it may be that that pretty much takes care of itself and really isn't a big deal. > > Certainly, in the general case, even if cleanup _is_ guaranteed, catching Errors is a bad idea because of the types of problems that they generally indicate. But there _are_ cases where catching Errors can be useful - the most prevalent one being catching AssertErrors in unit tests. Anyone trying to use a fancier unit testing framework (which I don't personally see any need for, but some people are _very_ interested in it - e.g. Jacob Carlborg) is going to have to do that. And in rare cases, even catching OutOfMemoryError makes some sense. You just have to know what you're doing and be careful. > > The _biggest_ thing with Errors is simply that they're not Exceptions, so > > catch(Exception e) {} > > won't catch them, and nothrow isn't affected by them. That right there provides their primary benefit. Personally, I'm on the fence as to whether attempting cleanup when an Error is thrown is a good idea or not. Certainly, in the unit testing case, it would certainly be desirable. Most of the rest, it probably doesn't matter all that much, but it would arguably be desirable to skip cleanup for them so that the amount of weird stuff that can happen on shutting down the program can be minimized. But of course, it also makes it harder to guarantee that certain things are done on shutdown, and it's not all that uncommon to have stuff that you want to _guarantee_ runs on shutdown, even if it's not a clean shutdown (or even _especially_ if it's not a clean shutdown). > > The other thing to consider is the fact that very few people seem to understand that Errors in flight aren't guaranteed to execute scope statements, destructors, or finally blocks. Well I've never heard that before, and I wrote the code on Windows to make sure that they do get executed. It's not an accident that it works. _______________________________________________ dmd-internals mailing list dmd-internals@puremagic.com http://lists.puremagic.com/mailman/listinfo/dmd-internals |
Copyright © 1999-2021 by the D Language Foundation