This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: catch(...) and forced unwind


On Wed, 2003-12-17 at 03:22, David Abrahams wrote:
> Mark Mitchell <mark@codesourcery.com> writes:
> 
> > (3) The thread_cancellation objects contain a reference count; copying
> > the object increments the reference count and destroying the object
> > decrements the reference count.  If the reference count goes to zero (as
> > would happen when the exception is caught and not rethrown) the thread
> > is uncancelled.
> 
> If "uncancelled" doesn't mean "re-deferred", I don't see the point of
> the reference count.  How is the "uncancelled" case different from
> the one where a copy of the thread_cancellation object is kept alive
> indefinitely, i.e.
> 
>   catch (posix::thread_cancellation& x)
>   { 
>      some_stack.push(new (nothrow) thread_cancellation(x));
>   }

I'm not sure exactly what you're getting at here.  

I was looking for semantics like auto_ptr.  

In particular, as long as you hold on to the thread_cancellation object
things are as if you are in a POSIX thread cancellation handler --
nothing is a cancellation point.

Consider this code:

  posix::thread_cancellation* p = NULL;
  try { } 
  catch (posix::thread_cancellation& x) {
    // Continue with the code outside this catch block, but 
    // as if we are in a thread cancellation handler.
    p = new (nothrow) posix::thread_cancellation (x);
    // x is destroyed as we leave this scope.
  }
  // Do some more stuff.
  ...
  // Now rethrow the cancellation exception.
  if (p)
    throw *p;

If, on the other hand, you let go of all references to the
thread_cancellation object, then you decided you do not want to be
cancelled at all.  (Think of that case like using longjmp to escape from
SIGTERM.)  In other words, if you do not copy "x" above, then you have
exited the cancellation handler mode; you are back to normal thread
operation as if you had never been cancelled.  (This is an extension to
POSIX thread semantics and is the bit that Ulrich objects to so
vehemently.)

> > With these changes, library designers can still say "catch all
> > exceptions", but they can also say "catch all exceptions except thread
> > cancellation".  
> 
> How?  You must mean something like:
> 
>   try { ... }
>   catch (posix::thread_cancellation&) { throw; }
>   catch (...) {
>         ... // here
>   }

Yes, that is what I mean.

> > It may be that catching all exceptions is a bad idea,
> > but that is up to the library designer.
> >
> > These changes are a pure extension to POSIX threads; they do not break
> > any existing POSIX threads code.  
> >
> > These changes are not quite a pure extension to standard C++; the C++
> > standard prohibits C library functions from throwing exceptions, so, for
> > example, "printf" cannot throw an exception.  
> 
> I'm not convinced there's a better solution, but it seems to me that
> this could be a disaster for the ability to freely use C++ libraries
> in threads without knowing something about their implementations.

I don't think there is any way to combine C++ and POSIX threads such
that all existing thread-safe, exception-safe C++ libraries will work in
the presence of cancellation.  Fundamentally, the idea of cancellation
means that you have to audit your entire library before using it in a
thread.  That's not news: the same problems come up with cooperating
processes in the presence of signals. A correct program, written for an
environment without SIGTERM, must be audited before being deployed in an
environment with SIGTERM.

My proposal gives two operating modes for the combination of POSIX
threads and C++. 

In one mode, the C++ thread sees a conforming C++ environment (i.e., one
in which "printf" never throws exceptions).  Stack unwinding will not be
done in that environment, so if such a thread is cancelled, it will not
get a chance to clean up after itself.  For many threads, that will be
OK, just as it is OK for many processes not to have a SIGTERM handler. 
In this situation, you have to audit your C++ code to make sure that it
is safe for it to go away without running cleanups.

In the other mode, the C++ thread sees a non-conforming environment, in
that some C library functions can throw exceptions.  In some cases,
simply writing exception-safe code around such calls may be good
enough.  In the general case, the library will have to be modified to
know about the idea of cancellation exceptions and take appropriate
action.  No surprise, that; lots of programs need modification to deal
with SIGTERM.  In this situation, you have to audit your C++ code to
make sure you've handled all the cancellation points correctly.

There's no way to avoid having to do an audit, but you can pick which
audit you have to do.

> Is it worth considering adding a mechanism which turns off
> cancellation exceptions during unwinding?  I realize it's a
> half-measure, but at least it would prevent some printf logging code
> from sending us to terminate.

I think this is a good suggestion.  It is orthogonal to my proposal,
though.

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]