
Hello, I am requesting comments, both for and against a "sticky exception". A sticky exception is one in which once thrown, is very hard to catch and handle, without an implicit rethrow. This represents some condition which the thrower believes is sufficiently severe that the application (or thread) should clean up resources, but not be allowed to continue, even with a catch (...) which does not rethrow. This can be implemented very simply with a class that owns the right to rethrow on destruct, and passes that right along within its copy constructor: class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} // sticky_exception(sticky_exception&& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} }; Please go ahead and experiment with such an exception. I would like to hear your experiences both good and bad. Here is one test driver: #include <iostream> void f3() { try { sticky_exception s; throw s; } catch (...) { std::cout << "catch in f3\n"; } std::cout << "f3 return\n"; } void f2() { try { f3(); } catch (...) { std::cout << "catch in f2\n"; } std::cout << "f2 return\n"; } void f1() { try { f2(); } catch (...) { std::cout << "catch in f1\n"; } std::cout << "f1 return\n"; } int main() { try { f1(); } catch (...) { std::cout << "catch in main\n"; } std::cout << "main return\n"; } which should output: catch in f3 catch in f2 catch in f1 catch in main and then terminate abnormally. Note that no function returned normally (no "return" statements printed). One possible variation of the above design is to place a reset() method in the exception which would cure its suicidal tendencies: class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} // sticky_exception(sticky_exception&& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} void reset() {owns_ = false;} }; One could then catch it and reset it: catch (sticky_exception& e) { e.reset(); std::cout << "sticky_exception fully handled\n"; } Note that with the above modification, catch (...) would still always automatically rethrow a sticky_exception. One would need to add the extra catch(sticky_exception&) shown above to get back the old behavior of catch (...). Do you see a use for such a class? Would you use it? Would you want libraries you're using to use it? Do you have real world example conditions which fit this use case? Or is it just evil (and why)? Thanks much, Howard

Howard Hinnant wrote:
I am requesting comments, both for and against a "sticky exception". A sticky exception is one in which once thrown, is very hard to catch and handle, without an implicit rethrow. This represents some condition which the thrower believes is sufficiently severe that the application (or thread) should clean up resources, but not be allowed to continue, even with a catch (...) which does not rethrow.
Here are a two arguments against: 1) The whole point of throwing an exception is that the program _can_ recover and not just clean up and exit. 2) If I write catch(...) in a destructor, I would be unpleasently surprised if it ends up throwing after all. What use cases do you have in mind anyway? Emil Dotchevski http://www.revergestudios.com/reblog/index.php?n=ReCode

On Sep 13, 2007, at 5:54 PM, Emil Dotchevski wrote:
Howard Hinnant wrote:
I am requesting comments, both for and against a "sticky exception". A sticky exception is one in which once thrown, is very hard to catch and handle, without an implicit rethrow. This represents some condition which the thrower believes is sufficiently severe that the application (or thread) should clean up resources, but not be allowed to continue, even with a catch (...) which does not rethrow.
Here are a two arguments against:
1) The whole point of throwing an exception is that the program _can_ recover and not just clean up and exit.
2) If I write catch(...) in a destructor, I would be unpleasently surprised if it ends up throwing after all.
What use cases do you have in mind anyway?
Thanks Emil. I'm conducting a personal sanity check. Some people just do this with a dipstick. I prefer to consult with my colleagues on boost. :-) I will definitely answer your question but I'd prefer not to for another day or two. I'm hoping to not influence other's answers one way or another for now. I'm exploring a design space of a sensitive subject, and saying more than I have risks tainting the unbiased responses I'm seeking. I very much appreciate you posting your arguments and hope others will do so as well. -Howard

Do you see a use for such a class? Would you use it? Would you want libraries you're using to use it? Do you have real world example conditions which fit this use case? Or is it just evil (and why)?
To be honest I don't see much use of such a class for user code. I could see a use for such a class for a framework, like you want to throw an exception so your framework users are notified, then you recup the exception later on. But I think that's fishy. Any examples I try to find for a use of the sticky exception makes me think that someone is trying to implement signals using exceptions. I'm curious to find out what you think a good use for the sticky exception would be. Philippe

Hello Howard, if I understood the semantics correctly, I see a particular situation where using such an exception could lead to hard to trace errors. Suppose I have the following setup: struct Error { ... }; struct SeriousRuntimeError : Error, sticky_exception { ... }; void log_error(Error const& error); And now somewhere within my program I have: try { // ... } catch (SeriousRuntimeError e) { // ... do something ... log_error(e); // ... do some more things ... } Up to now all is fine and the catch handler behaves as intended. Now suppose the maintainer of the Logging functions for whatever reason changes the log_error() signature to expect it's argument by value. All of a sudden the call to the log_error() function would rethrow the exception upon it's completion, thus skipping the remainder of the catch-handler. Of course I could circumvent this situation by using the proposed reset() method, but since sticky_exceptions' use in the class hierarchy might not be obvious to the user, I think it's bound to cause problems in collaborative situations. Best regards, Kimon

Howard, This is very interesing idea. I am not sure if it is very useful as part of widely available framework, but there are the cases when I wished to have "uncatchable" nuclear exception (preferably able to print out "I am caught in file@line", and then get thrown again). I dealt with a bunch of old code that is so poisoned with catch(...) statements, and I wish I had this idea about self-re-throwing exception so I can trace some parts of it more easily. Also, such an exception probably is very useful to debugging, so you can insert it and control "retrowness" dynamically. And last, but not least, it is probably usable as sort of cancelable "exit()" call (or thread_exit()?). This is a nice way to get your stack folded, destructors called all the way up, and you can intercept it and reset somewhere on top. Of course, above is my 2 cents Thanks, Andrey On Thu, 13 Sep 2007 12:23:01 -0600, Howard Hinnant <hinnant@twcny.rr.com> wrote:
Hello,
I am requesting comments, both for and against a "sticky exception". A sticky exception is one in which once thrown, is very hard to catch and handle, without an implicit rethrow. This represents some condition which the thrower believes is sufficiently severe that the application (or thread) should clean up resources, but not be allowed to continue, even with a catch (...) which does not rethrow.
This can be implemented very simply with a class that owns the right to rethrow on destruct, and passes that right along within its copy constructor:
class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} // sticky_exception(sticky_exception&& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} };
Please go ahead and experiment with such an exception. I would like to hear your experiences both good and bad. Here is one test driver:
[skip]
Note that with the above modification, catch (...) would still always automatically rethrow a sticky_exception. One would need to add the extra catch(sticky_exception&) shown above to get back the old behavior of catch (...).
Do you see a use for such a class? Would you use it? Would you want libraries you're using to use it? Do you have real world example conditions which fit this use case? Or is it just evil (and why)?
Thanks much, Howard
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

I think this idea could shine in designs where there is dependency injection, and the injected code needs to signal an abort of such a kind that no implementation of the primary code would be able to circumvent. Consider a unit testing framework used for some production unit tests. The tests "top" invokes production code, which in turn invokes injected code. The injected code finds a problem. Now the error needs to be communicated up, all the way to the framework at the top through the production code, without things like "catch(...)" in production code circumventing this process. What the top could do is another story - use its tricky knowledge how to disarm the sticky exception, or simply produce enough of a graceful output and still abort... But I think this would be useful, although very counter-intuitive for those who haven't familiarized themselves with the idea. Leo.

on Thu Sep 13 2007, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
Hello,
I am requesting comments, both for and against a "sticky exception". A sticky exception is one in which once thrown, is very hard to catch and handle, without an implicit rethrow. This represents some condition which the thrower believes is sufficiently severe that the application (or thread) should clean up resources, but not be allowed to continue, even with a catch (...) which does not rethrow.
The only purpose I have ever heard for such a thing would be to handle programming and critical data integrity errors (which in practice are due to programming errors). Exeptions are for *recoverable* conditions. I'm strongly against a sticky exception unless there's a way to label particular cleanups as "critical" and have only those executed when a sticky exception is thrown. Most cleanups are noncritical, and in the presence of integrity problems, can cause problems and interfere with the critical recovery actions that you really do want to execute. Furthermore, it tends to reinforce the misconception that exceptions are a good way to handle nonrecoverable conditions. Frankly there are probably better ways to handle this than by throwing a special exception. Critical cleanups could be registered/unregistered as atexit() actions. Did I mention recently that turning a logic error into an exception is a bad idea (http://tinyurl.com/2ph58t) and we have no programming model for handling it? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Hi Howard, Howard Hinnant wrote:
class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} // sticky_exception(sticky_exception&& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} };
Does this destructor not invoke some kind of recursive nuttiness that leads to std::terminate()? If a sticky_exception is created on "the stack", any throw statements made thereafter in the same scope will invoke the destructor of that sticky_exception, at which point we end up doing this (calling ~sticky_exception) all over again. I though 15.5.1 would apply, here? "In the following situations exception handling must be abandoned for less subtle error handling techniques: [...] -- when the destruction of an object during stack unwinding (15.2) exits using an exception [...] In such cases, void terminate(); is called (18.6.3)." For example, the following program prints "genesis!" and "terminating!", but not "death!" (at least with MSVC8, gcc 3.4.5, gcc 4.2.1 and DMC 8.42n. Borland bcc32 5.82 disagrees): #include <exception> #include <iostream> class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} }; struct noisy { noisy() { std::cerr << "genesis!\n"; } ~noisy() { std::cerr << "death!\n"; } }; void handler() { std::cerr << "terminating!\n"; } int main() { std::set_terminate(handler); { noisy n; sticky_exception s; throw s; } return 0; } So how is creating a sticky_exception any different to a glorified call to std::terminate() (at the end of scope)? Or am I missing something crazy-obvious? Cheers, Edd

On Sep 13, 2007, at 2:23 PM, Howard Hinnant wrote:
class sticky_exception { bool owns_; sticky_exception& operator=(const sticky_exception&); public: sticky_exception() : owns_(true) {} sticky_exception(sticky_exception& s) : owns_(s.owns_) {s.owns_ = false;} // sticky_exception(sticky_exception&& s) : owns_(s.owns_) {s.owns_ = false;} ~sticky_exception() {if (owns_) {sticky_exception s; throw s;}} };
I promised to explain myself after a day or two and that time has come. Thanks much to everyone who has responded. sticky_exception is not my brain child. It is modeling a behavior that someone else has proposed and I have been attempting to explore the ramifications of that behavior in as unbiased manner as possible. I did not want to mention the specific application because the application in question brings with it all manner of history and biased baggage. The application is thread cancellation. One might want to call this thread interruption or whatever. Thread interruption (I shall use the term interruption as opposed to cancellation for really no good reason) is essentially an exception. The stack gets unwound, executing "landing pads" on the way up. Those "landing pads" in C++ are destructors and catch clauses. There exist opinions that the exception representing this action be "uncatchable". So my question to boost was: What will this mean to code in general, without thinking specifically about interruption? I have attempted to present the uncatchable exception in such a manner that my position on it was not clear (in the hopes of receiving less biased comments). However, now that the cat is out of the bag anyway, my own concern centers around things like: class A { public: ... ~A() { try { do something that might throw } catch (...) { log or delay the exception } } }; ... void foo() { std::vector<A> v; ... } If ~A() is really not nothrow (even though it appears to be), then std::vector is really not going to work very well in an exceptional condition (i.e. it will leak A's). I.e. I have grave concerns over sticky_exception myself, even if its only application is thread interruption. -Howard

On 9/15/07, Howard Hinnant <hinnant@twcny.rr.com> wrote:
On Sep 13, 2007, at 2:23 PM, Howard Hinnant wrote:
I promised to explain myself after a day or two and that time has come. Thanks much to everyone who has responded.
The application is thread cancellation. One might want to call this thread interruption or whatever. Thread interruption (I shall use the term interruption as opposed to cancellation for really no good reason) is essentially an exception. The stack gets unwound, executing "landing pads" on the way up. Those "landing pads" in C++ are destructors and catch clauses.
If ~A() is really not nothrow (even though it appears to be), then std::vector is really not going to work very well in an exceptional condition (i.e. it will leak A's). I.e. I have grave concerns over sticky_exception myself, even if its only application is thread interruption.
The other 'example' of sticky exceptions, that we all accept as valid, is throwing inside a constructor - you can't catch a base class constructor throw and handle it in the derived class' constructor. I bring that up as a somewhat related example of 'sticky' exceptions. Not that I think the thread case should necessarily follow the same idea. Tony

on Sat Sep 15 2007, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
There exist opinions that the exception representing this action be "uncatchable".
I think you mean "unstoppable." -- Dave Abrahams Boost Consulting http://www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

I see that I'm slightly late in responding, but I'll mention this anyway: In the proposed threading model for C++, what happens on an uncaught exception in a thread? Is that still terminate? Or is only that thread killed off? (I think I remember seeing discussion in C++ committee meeting minutes or a proposal that indicated the former, but haven't been tracking such things closely. Boost.Thread was relatively recently changed from the latter to the former.) Assuming uncaught exception in a thread => terminate, then the sticky_exception only permits cleanup in the throwing thread and not in any other threads, which doesn't seem to me to be a whole lot more useful than an immediate termination rather than the initial throw.

On Sep 15, 2007, at 5:42 PM, Kim Barrett wrote:
I see that I'm slightly late in responding, but I'll mention this anyway:
In the proposed threading model for C++, what happens on an uncaught exception in a thread? Is that still terminate? Or is only that thread killed off? (I think I remember seeing discussion in C++ committee meeting minutes or a proposal that indicated the former, but haven't been tracking such things closely. Boost.Thread was relatively recently changed from the latter to the former.)
Assuming uncaught exception in a thread => terminate, then the sticky_exception only permits cleanup in the throwing thread and not in any other threads, which doesn't seem to me to be a whole lot more useful than an immediate termination rather than the initial throw.
The intent of http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2320.html is that a thread which ended in an uncaught exception would terminate() the application *except* for the thread_interrupt exception (or exceptions derived from it) which would be silently swallowed within the runtime such that just that thread would die. Whether that intent represents popular support on the committee at this time or not is not something I have a good feel for. -Howard

My personal opinion on this is that the thread_interrupted exception should be considered a normal exception. My guideline to use exception vs. assertions is: * if an invariant is broken, use assert * if inputs are out of range, use exceptions, or better exit codes * if the postcondition cannot be guaranteed due to a failure in the interaction with external world, use exceptions I think thread cancellation is an (asynchronous) interaction with external world, so it should be handled exactly in the same way as a timeout connecting to a TCP address, or an error when reading from disk. Moreover, I suggest that, instead of speaking of cancellation, in which the outcome is decided from outside, we should speak of interruption (like in Java, where thread handling was designed together with the language). Interruption is signalling a thread (that may be blocked into a long operation) to become responsive again to some predefined events (one of those, but not the only one, is termination). Corrado -- __________________________________________________________________________ dott. Corrado Zoccolo mailto:zoccolo@di.unipi.it PhD - Department of Computer Science - University of Pisa, Italy -------------------------------------------------------------------------- The self-confidence of a warrior is not the self-confidence of the average man. The average man seeks certainty in the eyes of the onlooker and calls that self-confidence. The warrior seeks impeccability in his own eyes and calls that humbleness. Tales of Power - C. Castaneda

On 9/13/07, Howard Hinnant <hinnant@twcny.rr.com> wrote:
I am requesting comments, both for and against a "sticky exception". [...] Do you see a use for such a class? Would you use it? Would you want libraries you're using to use it? Do you have real world example conditions which fit this use case? Or is it just evil (and why)?
I think it might be a little too much on the evil side. Wouldn't it break otherwise exception safe (if admittedly not very elegant) code like the following? template<typename F> bool foo(F f) { bar = acquire-resource-bar() bool failed = false; try { f(); } catch (....) { failed = true; } release-resource-bar(bar); return failed; } Foo would work correctly for any function-like objects, except for those that throw sticky exception. void baz() { sticky_exception s throw s; } int main() { foo(baz); // XXX leaks a bar!! } That is, you can't throw a sticky exception unless everything in the call stack is capable of handling it, else you might leak. I know that the preferred idiom for exception safety is RAII (Which is also sticky-exception-safe), but sometimes it is cumbersome to write a RAII wrapper just for one use. HTH, gpd
participants (12)
-
Andrey Tcherepanov
-
Corrado Zoccolo
-
David Abrahams
-
Edd Dawson
-
Emil Dotchevski
-
Giovanni Piero Deretta
-
Gottlob Frege
-
Howard Hinnant
-
Kim Barrett
-
Kimon Hoffmann
-
Leo Landa
-
Philippe Vaucher