Boost logo

Boost Users :

From: Alexander Terekhov (yg-boost-users_at_[hidden])
Date: 2003-05-15 05:09:49


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 <stdio.h> // The POSIX one
        #include "errbutt" // That's my own

        int main() {
          int rc;
          rc = printf("Hello World\n");
          assert(rc);
          if (rc < 0 || (rc = fclose(stdout)) == EOF) {
            rc = errno;
            assert(rc);
          }
          else {
            assert(!rc);
          }
          return rc ? report_error(rc) : 0;
        }

;-)
                            
> 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

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net