
Mark Sizer wrote:
That's interesting.
Is it really necessary to have a release_guard that re-locks in the destructor? What's the point?
The point is to save some key strokes, basically.
I could read the spec, but I'll ask instead: Isn't the compiler allowed to optimize away some scoping?
The compiler is not allowed to do that (see 3.7.2/3).
Is it required that objects be destructed in the order they were constructed (if they're in the same stack frame)?
See 6.7/2 and 6.6/2.
If an exception is thrown ...
Then rather interesting things COULD happen (please try not to miss the "it's time to fix the standard" link below ;-) ). <Forward Inline> -------- Original Message -------- Newsgroups: comp.programming.threads Subject: Re: __attribute__((cleanup(function)) versus try/finally David Butenhof wrote: [...]
--- in C/POSIX module ---
void cleanup(void *) { printf("hello\n"); }
void c_f() { pthread_cleanup_push(cleanup, 0); pthread_exit(0); pthread_cleanup_pop(0); }
--- in C++ 'main' module ---
struct object { ~object() { printf("hello\n"); } };
void f() throw() { object o; c_f(); }
int main() { f(); }
How many times will we see "hello"?
I see it three times, because that's how many times you typed it. (Was that a trick question? ;-) )
;-)
Seriously, though, I think that's very much the crux of your argument, isn't it?
Yep.
I would expect "hello" to be output by the POSIX cleanup handler as c_f() is unwound in response to pthread_exit(). THAT is clearly specified by current standards.
NO! I can see nothing in the POSIX standard that would prohibit the following implementation of pthread_exit(): extern "C" void pthread_exit(void * ptr) { std::thread_exit(ptr); } using something along the lines of: (from the "std" namespace) class thread_termination_request : public std::exception ... class thread_cancel_request : public std::thread_termination_request ... class thread_exit_request : public std::thread_termination_request ... template<typename T> class thread_exit_value : public std::thread_exit_request ... template<typename T> void thread_exit(T value) { assert(std::thread_self().can_exit_with<T>()); throw thread_exit_value(value); } < as an aside: Attila, do you follow me? > I see almost-no-problems** catching and "finalizing" ANY of these exceptions. If one can catch-and-finalize "thread termination" and cause an abnormal process termination right after "finalizing" it, I don't see why this can't be done by the implementation at throw point due to ES violation. **) http://www.opengroup.org/austin/mailarchives/austin-group-l/msg05202.html (Subject: XSH ERN 77, 81 and 82)
Now, though, we face what may be a philosophical issue, if it's not carefully tied down by the C++ standard (which I haven't read, much less analyzed in detail). If C++ is required to have 2-phase exceptions AND if
It isn't required, currently.
an implementation is not allowed to SEARCH (phase 1) through f()'s empty throw() specification, then I would expect to see std::unexpected() fire before the second "hello" can be written by o's destructor.
NO! Before the FIRST "hello"! (I know that you know that the answer I wanted is a sort of /pthread_/null of "hello" ;-) ). Because the "handler" inject by pthread_cleanup_push() shall be modeled upon a C++ destructor, not archaic try/finally.
However, you have suggested that a single phase unwind implementation is allowed, and in such an implementation I would expect o's destructor to run, printing a second "hello", and THEN for std::unexpected() to fire as the runtime attempts to unwind through the empty throw() specification.
Yes. Well, please take a loot at: http://groups.google.com/groups?selm=3EC0ECAA.6520B266%40web.de (Subject: Exception handling... it's time to fix the standard)
I can tell you that the C++ implementation on both Tru64 UNIX and OpenVMS, compiled and run with default options, print "hello" twice and THEN abort with unexpected().
That's exactly what the C++ standard currently mandates, so to speak.
(Proving that, as I said, while OpenVMS has always supported 2-phase exceptions and recommends cleanup on the unwind phase, not everyone actually uses it that way. ;-) )
Yeah, unfortunately, that's seems to be true with respect to majority of C++ committee members too.
I can also say that on Tru64 UNIX (I didn't bother going through the extra gyrations to get and analyze a process core dump on OpenVMS), the core file leads one to the f() frame (though there's terminate and abort and raise and all that stuff on top of it) so that someone diagnosing the abort might be lead to examine the f() function and notice the empty throw() specification. (While OpenVMS reports that "terminate or unexpected" has been invoked, on Tru64 you get only "Resources lost(coredump)".)
This at least meets my minimal requirements, that the cleanup and unwind be properly synchronized. I'd be vexed at an implementation that maintained a separate stack of cleanup handlers, for example, and called them without actually unwinding the call stack, so that the final core file might show c_f() as the active frame even though cleanup had occurred out through f(). I'd be annoyed if the core file showed NO active frames, because there'd be no clue to what happened.
Yes.
I also understand that YOU would prefer that std::unexpected() would fire without running ANY cleanup OR unwinding any frames as the SEARCH exception pass (phase 1) ran up against the empty throw() specification.
YES!
I'm inclined to agree with you philosophically, but with one foot (plus a heel of the other foot) planted firmly in the real world, I'd worry about the consequences of breaking external invariants by suddenly adding a requirement that ~o() not be run in this case. Even your trivial example shows where this could cause problems, because you do have an external invariant. The output of this program might be redirected into a file that might be used as a benchmark for testing (or for other purposes),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Oh, http://groups.google.com/groups?selm=3EA6B22B.50C5B37%40web.de
#include <cassert> // The C++ stuff
#include
and the change in output from "hello\nhello\n" to "hello\n" between one version of the C++ runtime and another could indeed be an issue.
Yes, I understand. But please note that propagation of exceptions (unwinding) when "no matching handler found" is implemention-defined (well, ES aside) in the current C++ standard. Portably, folks just can't rely on always-unwind... if they don't use catch(...) and/or "current" version of ES. The funny thing is that exceptions specs are considered sort-of "harmful" by many "prominent" members of the C++ community and aren't recommended. http://www.gotw.ca/publications/mill22.htm http://www.boost.org/more/lib_guide.htm#Exception-specification regards, alexander. -- http://groups.google.com/groups?selm=3D3C0BCA.A5E2F8B2%40web.de