digitalmars.D.learn - Detecting exception unwinding
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (7/7) Feb 03 2016 Is there some reliable way to detect that a destructor is called
- Jonathan M Davis via Digitalmars-d-learn (9/16) Feb 03 2016 AFAIK, there is no way to detect whether an exception is in flight or no...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/16) Feb 03 2016 :-/
- Jonathan M Davis via Digitalmars-d-learn (8/24) Feb 03 2016 Yeah, and I'm pretty sure that we don't have anything like that at this
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/9) Feb 04 2016 Thanks, I think I will wait with filing an enhancement request as
- =?UTF-8?Q?Ali_=c3=87ehreli?= (5/18) Feb 03 2016 std::uncaught_exception used to be considered useless:
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (4/7) Feb 04 2016 I've read that one, but Herb Sutter does not provide an argument,
- Jonathan M Davis via Digitalmars-d-learn (6/7) Feb 04 2016 I think that the only case I've ever had for it was for a unit testing
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (10/18) Feb 04 2016 Well, yes, it is useful for logging. It is useful to know if an
- Jacob Carlborg (16/23) Feb 03 2016 I don't have an good solutions, but a few ideas that you could try:
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (4/9) Feb 03 2016 Yes, if there is no such feature planned then maybe creating a
- cy (79/86) Feb 04 2016 I think you might be talking about two very different concepts
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (15/18) Feb 05 2016 The object itself is the scope (RAII). If you can test for
- cy (13/15) Feb 05 2016 Sorry, years of python programming have made me shy of
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (10/14) Feb 06 2016 That is error-prone!
- cy (16/17) Feb 06 2016 And D supports the "with" statement in python, in the form of
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/10) Feb 07 2016 Not sure what you mean by that. Destructors shall not be called
Is there some reliable way to detect that a destructor is called because of exception unwinding? I basically want to change behaviour within a destructor based on whether the destructor is called as a result of a regular or an exceptional situation. E.g. commit changes to a database on regular destruction, or inhibit logging during exception unwinding.
Feb 03 2016
On Wednesday, February 03, 2016 11:09:00 Ola Fosheim Grřstad via Digitalmars-d-learn wrote:Is there some reliable way to detect that a destructor is called because of exception unwinding? I basically want to change behaviour within a destructor based on whether the destructor is called as a result of a regular or an exceptional situation. E.g. commit changes to a database on regular destruction, or inhibit logging during exception unwinding.AFAIK, there is no way to detect whether an exception is in flight or not aside from the cases where scope(failure) or catch would catch the exception, and from what I recall of the last time that someone asked this question, the consensus was that it couldn't be done - but maybe I'm remembering incorrectly. I am pretty sure that this was asked within the last few months though, so if you search D.Learn, you can probably find that discussion. - Jonathan M Davis
Feb 03 2016
On Wednesday, 3 February 2016 at 11:41:28 UTC, Jonathan M Davis wrote:AFAIK, there is no way to detect whether an exception is in flight or not aside from the cases where scope(failure) or catch would catch the exception, and from what I recall of the last time that someone asked this question, the consensus was that it couldn't be done - but maybe I'm remembering incorrectly. I am pretty sure that this was asked within the last few months though, so if you search D.Learn, you can probably find that discussion.:-/ I am looking for something like this: http://en.cppreference.com/w/cpp/error/uncaught_exception It is useful for certain types of libraries where you want to cancel out effects "undo commits" when exceptional situations arise.
Feb 03 2016
On Wednesday, February 03, 2016 11:47:35 Ola Fosheim Grřstad via Digitalmars-d-learn wrote:On Wednesday, 3 February 2016 at 11:41:28 UTC, Jonathan M Davis wrote:Yeah, and I'm pretty sure that we don't have anything like that at this point. Feel free to create an enhancement request for it: https://issues.dlang.org At least that way, it's kept track of, though I certainly have no idea when it might be implemented (presumably when someone needs it enough that they take the time to do so). - Jonathan M DavisAFAIK, there is no way to detect whether an exception is in flight or not aside from the cases where scope(failure) or catch would catch the exception, and from what I recall of the last time that someone asked this question, the consensus was that it couldn't be done - but maybe I'm remembering incorrectly. I am pretty sure that this was asked within the last few months though, so if you search D.Learn, you can probably find that discussion.:-/ I am looking for something like this: http://en.cppreference.com/w/cpp/error/uncaught_exception It is useful for certain types of libraries where you want to cancel out effects "undo commits" when exceptional situations arise.
Feb 03 2016
On Wednesday, 3 February 2016 at 21:35:38 UTC, Jonathan M Davis wrote:https://issues.dlang.org At least that way, it's kept track of, though I certainly have no idea when it might be implemented (presumably when someone needs it enough that they take the time to do so).Thanks, I think I will wait with filing an enhancement request as I think there are more pressing issues to be dealt with. I should look into creating my own runtime anyway.
Feb 04 2016
On 02/03/2016 03:47 AM, Ola Fosheim Grøstad wrote:On Wednesday, 3 February 2016 at 11:41:28 UTC, Jonathan M Davis wrote:std::uncaught_exception used to be considered useless: http://www.gotw.ca/gotw/047.htm Does that apply to D? AliAFAIK, there is no way to detect whether an exception is in flight or not aside from the cases where scope(failure) or catch would catch the exception, and from what I recall of the last time that someone asked this question, the consensus was that it couldn't be done - but maybe I'm remembering incorrectly. I am pretty sure that this was asked within the last few months though, so if you search D.Learn, you can probably find that discussion.:-/ I am looking for something like this: http://en.cppreference.com/w/cpp/error/uncaught_exception It is useful for certain types of libraries where you want to cancel out effects "undo commits" when exceptional situations arise.
Feb 03 2016
On Thursday, 4 February 2016 at 07:55:42 UTC, Ali Çehreli wrote:std::uncaught_exception used to be considered useless: http://www.gotw.ca/gotw/047.htm Does that apply to D?I've read that one, but Herb Sutter does not provide an argument, just a claim that having different semantics on the exceptional path to be somehow immoral.
Feb 04 2016
On Wednesday, February 03, 2016 23:55:42 Ali Çehreli via Digitalmars-d-learn wrote:std::uncaught_exception used to be considered useless:I think that the only case I've ever had for it was for a unit testing framework where I wanted to use RAII to print something when the tests failed (and thus an exception was thrown) but not print if they succeeded, and if I were to do that in D, I'd just use scope(failure). - Jonathan M Davis
Feb 04 2016
On Thursday, 4 February 2016 at 10:03:13 UTC, Jonathan M Davis wrote:On Wednesday, February 03, 2016 23:55:42 Ali Çehreli via Digitalmars-d-learn wrote:Well, yes, it is useful for logging. It is useful to know if an invariant is broken during regular unwinding (serious error) or exceptional situations (might be unavoidable). But I think Herb Sutters major point was that if you had multiple destructors it could not detect where the exceptions originate from. So C++17 has a new function that returns the number of uncaught expressions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdfstd::uncaught_exception used to be considered useless:I think that the only case I've ever had for it was for a unit testing framework where I wanted to use RAII to print something when the tests failed (and thus an exception was thrown) but not print if they succeeded, and if I were to do that in D, I'd just use scope(failure).
Feb 04 2016
On 2016-02-03 12:09, Ola Fosheim Grøstad wrote:Is there some reliable way to detect that a destructor is called because of exception unwinding? I basically want to change behaviour within a destructor based on whether the destructor is called as a result of a regular or an exceptional situation. E.g. commit changes to a database on regular destruction, or inhibit logging during exception unwinding.I don't have an good solutions, but a few ideas that you could try: * Try core.runtime.Runtime.traceHandler [1]. I'm assuming the trace handler will be called at some point in time if an exception has been thrown * If you're on Linux or FreeBSD and using HEAD you could, I think, use "__cxa_current_exception_type" or "__cxa_uncaught_exception" since DMD now supports DWARF exception * Worst case scenario, override "_d_throwc" [2] For the trace handler and overriding "_d_throwc" you would just use the default implementation plus store a boolean (or counter) in a TLS variable indicating an exception has been thrown. [2] https://github.com/D-Programming-Language/druntime/blob/1f957372e5dadb92ab1d621d68232dbf8a2dbccf/src/rt/deh_win64_posix.d#L213 -- /Jacob Carlborg
Feb 03 2016
On Wednesday, 3 February 2016 at 12:44:39 UTC, Jacob Carlborg wrote:* Worst case scenario, override "_d_throwc" [2] For the trace handler and overriding "_d_throwc" you would just use the default implementation plus store a boolean (or counter) in a TLS variable indicating an exception has been thrown.Yes, if there is no such feature planned then maybe creating a new runtime is the most sensible option.
Feb 03 2016
On Wednesday, 3 February 2016 at 11:09:00 UTC, Ola Fosheim Grøstad wrote:Is there some reliable way to detect that a destructor is called because of exception unwinding? I basically want to change behaviour within a destructor based on whether the destructor is called as a result of a regular or an exceptional situation. E.g. commit changes to a database on regular destruction, or inhibit logging during exception unwinding.I think you might be talking about two very different concepts here. Unwinding only happens within the context of a certain scope. That's where it gets the backtrace from. If you construct an object, then save it somewhere globally, then return from the function, then go back into the event loop, then whatever... you no longer have your original scope. There can be no exceptional situation, nor can there be "regular destruction" because the scope has already unwound. Saving your object globally keeps it from being destructed, and you might use reference counting in that case I guess, but ultimately, when an exception occurs, your object will have nothing to do with it at all. That might be your situation, in which case you simply do this: bool fail = false; ... class Foo { ... ~this() { if(!fail) writeln(shakespeare); ... } ... } int main() { scope(failure) fail = true; ... } When your program exits due to an uncaught exception, it can't unwind higher than main, so "scope(failure)" for main will apply to all uncaught exceptions that kill the program. Any globally stored variables destructed after that will see fail as being true. The other situation is easier, and probably what you're trying to do, so sorry for wasting your time. If you have a local variable in a local scope, then when you leave that scope normally, the variable will be destroyed, as well as when you fail out of it. You want to find out whether you are leaving that scope normally in the destructor, and it's not anything about whether the program is dying or not, but instead it's making sure your local objects with global state clean up before they die. If that's what you're doing then you do this: void some_routine() { ... Foo foo; scope(failure) foo.fail = true; ...proceed normally... } When some_routine exits normally, foo has not set fail, and it will be destroyed knowing that. When some_routine errors out, "scope(failure)" will set the fail on foo, and then when foo is destroyed it can do so quietly. But again, two different situations. If you try to do this: Foo foo; void some_routine() { scope(failure) foo.fail = true; ...no exceptions... } void failure_routine() { ... throw new SomeException(); ... } int main() { some_routine(); failure_routine(); } ...then fail will never be set, since you exited some_routine before throwing any exceptions. Thus why you set "scope(failure)" on the main function, if you're concerned about globals being destructed due to program failure. You set "scope(failure)" on the local function when you're concerned about locals complaining as they destruct when the function returns from an exceptional situation. If you want the former and do the latter, then your globals will not see that any exception occurred. If you want the latter and do the former, then any local variables will destruct long before main reaches "scope(failure)" YMMV. I haven't tested any of this, and I'm kind of shaky at D too.
Feb 04 2016
On Friday, 5 February 2016 at 07:31:24 UTC, cy wrote:I think you might be talking about two very different concepts here. Unwinding only happens within the context of a certain scope.The object itself is the scope (RAII). If you can test for "uncaught_exceptions" you can implement the equivalent of scope(failure/success) etc within destructors. So you basically should be able to do: { Database db ...; for(...){ db.Transaction t = db.new_transaction(....) // transaction t is closed here by destructor // but should not commit if exception is unwinding } // db is closed here but should log different information for exceptions }
Feb 05 2016
On Friday, 5 February 2016 at 08:16:05 UTC, Ola Fosheim Grøstad wrote:If you can test for "uncaught_exceptions" you can implement the equivalent of scope(failure/success) etc within destructors.Sorry, years of python programming have made me shy of destructors. It just looks a little less "magic" to me if I specify the destruction explicitly after creating the object, using the "scope(exit)" syntax. "scope(success)" and "scope(failure)" have no analogy in destructors as far as I know. Destructors just destroy the object, and doing more than that is risking weird edge conditions. But again, if you already have your elaborate destructor, you can set "o.fail = true" in a "scope(failure)" statement, and the destructor just has to check this.fail for how it's being destroyed..
Feb 05 2016
On Saturday, 6 February 2016 at 06:08:41 UTC, cy wrote:Sorry, years of python programming have made me shy of destructors. It just looks a little less "magic" to me if I specify the destruction explicitly after creating the object, using the "scope(exit)" syntax.That is error-prone! In Python you are supposed to use the with-statement to get RAII like behaviour. So you use __enter__ and __exit__ methods on the context-manager. From the Python docs: «The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.» See, even Python supports this. :-)
Feb 06 2016
On Saturday, 6 February 2016 at 14:25:21 UTC, Ola Fosheim Grøstad wrote:See, even Python supports this. :-)And D supports the "with" statement in python, in the form of "scope()" statements. The D way is slightly less misleading too, as with somethingThatFails() as e: print(e) doesn't make it obvious that neither __enter__ nor __exit__ will be called if you raise an exception in the creation of the object, or that if you raise an exception in __enter__, then __exit__ won't be called. auto e = somethingThatFails() scope(failure) cleanup(e); makes more sense to me, since it's blatantly obvious that the construction (and entering) process isn't covered by the cleanup routine.
Feb 06 2016
On Saturday, 6 February 2016 at 22:51:45 UTC, cy wrote:auto e = somethingThatFails() scope(failure) cleanup(e); makes more sense to me, since it's blatantly obvious that the construction (and entering) process isn't covered by the cleanup routine.Not sure what you mean by that. Destructors shall not be called if constructors fail, constructors should clean up themselves. "scope(failure)" is useful for C-like APIs, but not a good replacement for RAII.
Feb 07 2016