www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Detecting exception unwinding

reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
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:
 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.
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 Davis
Feb 03 2016
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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:
 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.
std::uncaught_exception used to be considered useless: http://www.gotw.ca/gotw/047.htm Does that apply to D? Ali
Feb 03 2016
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
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
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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:
 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).
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.pdf
Feb 04 2016
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
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. [1] http://dlang.org/phobos/core_runtime.html#.Runtime.traceHandler [2] https://github.com/D-Programming-Language/druntime/blob/1f957372e5dadb92ab1d621d68232dbf8a2dbccf/src/rt/deh_win64_posix.d#L213 -- /Jacob Carlborg
Feb 03 2016
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
prev sibling parent reply cy <dlang verge.info.tm> writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
parent reply cy <dlang verge.info.tm> writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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
parent reply cy <dlang verge.info.tm> writes:
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
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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