[3.3] Followup to C++ forced unwinding

Mark Mitchell mark@codesourcery.com
Wed Apr 30 22:54:00 GMT 2003


> > One key question that I'm not sure got resolved on the thread was what
> > happens in this case:
> > 
> >    try { 
> >      // Something that might:
> >      // (a) result in cancellation.
> >      // (b) result in a foreign exception being thrown.
> >      // (c) result in longjmp_unwind being called.
> >    } catch (...) {
> >      // There is no "throw;" here to rethrow.
> >    }
> > 
> > Presumably (a) is the same as either (b) or (c).  Which one?
> 
> ... I thought (a) was similar to (c) and that the catch 
> handler here is *not* invoked in either case.

Oh, bummer.  

I thought (a) was going to be similar to (b), not (c).

I think the key design goal should be to make correct single-threaded
code usable in a multi-threaded program, with as little modification as
possible.  

(Rationale: if I have a library, obtained from somewhere, I want to be
able to use it in my multi-threaded program without having to take it
apart bit by bit to see if it's safe.)

Nathan Myers and I were just discussing this, and we agreed that running
the "catch (...)" handlers is the Right Thing -- at least for thread
cancellation and foreign exceptions.  (I've got less of an opinion about
longjmp_unwind; the longmp-ness and the unwind-ness are at odds.)

If we don't let "catch (...)" handlers run, we're violating a basic C++
assumption, with the result that lots of real code will work in a
single-threaded environment, but suddenly not work in a threaded
environment.

If the catch-clause chooses not to rethrow the exception, that just
means this thread isn't going to be cancelled, which could happen from
an ordinary pthread cancellation handler.  Deferred cancellation
semantics don't give you any guarantees about when a thread will exit
after it has been cancelled.

Therefore, I'd rather we just treat cancellation like a foreign
exception.

> > And, if which of these cases are we implicitly rethrowing from the
> > catch, even though the user didn't write "throw;"?  
> 
> We don't implicitly rethrow, because we don't allow the catch.
> I've thought about this a bit, and have reservations because...
> 
> > The reason I ask is that:
> > 
> >   f = fopen(...);
> >   try {
> >   } catch (...) {
> >   }
> >   fclose (f);
> > 
> > looks to a C++ programmer to be a perfectly safe piece of code, modulo
> > longjmp.
> 
> ... of this.  It *isn't* safe unless you know that nothing in the
> catch handler can throw.  If we can get folks to write this as
>
> 	struct closer {
> 	  FILE *f;
> 	  closer(FILE *x) : f(x) { }
> 	  ~closer() { if (f) fclose (f); }
> 	};

Well, yes, naturally.  But people don't always do it that way, and there
are even sometimes good reasons. :-)

(This whole signals/cancellation/exception thing is really an
interesting topic; I recently co-authored a paper about how Python's
exception-model is unsafe with respect to signals, even though the
language is designed to synchronize signals in such a way that you can
throw an exception from a signal handler.  This is not an interpreter
bug; it's a fundamental language design bug.)

> > Does the IA-64 ABI specify this?
> 
> No.  It says things can work either way, which is a bit unsatisfying.

Indeed.

> > But, I'd be unhappy if foreign exceptions and/or thread cancellation did
> > an implicit rethrow.
> 
> Ok, good, we're on the same page.

Sort of -- it sounds like we agree about the implicit rethrow, but not
about whether or not the catch(...) clauses are actually entered or not.

-- 
Mark Mitchell <mark@codesourcery.com>
CodeSourcery, LLC



More information about the Gcc-patches mailing list