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


> Yes, it's that topic again.  The previous discussion can be found here:
>
>   http://gcc.gnu.org/ml/gcc-patches/2003-04/threads.html#02246
>   http://gcc.gnu.org/ml/gcc-patches/2003-05/threads.html#00000
>
> I've tried to CC everyone who contributed to the earlier discussion.  
>
> It's coming up again because it turns out that the obvious implementation
> of the iostream inserters and extractors involves a catch(...) which does
> not rethrow, so the question of what to do with such a catch block has
> significant practical consequences.
>
> To recap, some folks thought that such a catch block should rethrow
> automatically, and some thought that it should work just like it does for
> any other exception, effectively deferring the cancellation until the next
> call to a cancel point.
>
> The argument for rethrow is that it's probably the right behavior.

Except when it isn't.  Think of the boundary between C++ and other
code which can't propagate exceptions.

> The argument for continue is that it's what the user wrote and we shouldn't
>   be changing the semantics of their code behind their back.

Sounds reasonable to me.

> The current compromise behavior is to call terminate so that the user is
>   aware of the problem.

How is that a compromise?  Because nobody gets what they want?  The
user's behavior is changed, and you don't rethrow.

> The relevant verbiage from the C++ standard:
>
>   27.6.1.1  Class template basic_istream                   [lib.istream]
>
> 4 If one of these called functions  throws  an  exception,  then  unless
>   explicitly  noted  otherwise,  the input function sets badbit in error
>   state.  If badbit is on in exceptions(), the  input  function rethrows
>   the exception without completing its actions, otherwise  it  does  not
>   throw anything and proceeds as  if  the called function had returned a
>   failure indication.
>
> As a result, the following testcase will abort under the compromise
> behavior:
>
>   #include <pthread.h>
>   #include <iostream>
>
>   void *
>   tf (void *)
>   {
>     while (true)
>       std::cout << "." << std::flush;
>     return NULL;
>   }
>
>   int
>   main (void)
>   {
>     pthread_t t;
>     while (true)
>       {
>         if (pthread_create (&t, NULL, tf, NULL))
>           break;
>         if (pthread_cancel (t))
>           break;
>         if (pthread_join (t, NULL))
>           break;
>       }
>     return 1;
>   }
>
> Clearly, this high-level behavior is wrong.  iostream code should be
> cancelable.  

I'm going to assume some things of which I have no knowledge:

1. Thread cancellation doesn't induce an asynchronous C++ exception in
   the thread.

2. There is some documentation somewhere which indicates that one of
   the functions called by tf is a "cancellation point" (i.e. may
   throw thread cancellation exceptions).  Otherwise, it seems to me,
   "this high-level behavior" would not be wrong.

> Either of the proposed behaviors would make this particular testcase work;
> the rethrow option would cause cancellation to finish sooner, but the
> continue option would only defer cancellation until the next
> flush.  

As I understand cancellation, it is only a request, so that's
entirely reasonable.

> If we left out the flush, however, the continue option would never
> actually cancel because the only cancel point in the loop is guarded
> by the catch(...) in the string inserter.
>
> I believe this is a bug in the library: it thinks it knows what sorts of
> exceptions it will see and wants to swallow all of them, but it is wrong.

It's definitely not a bug if you're just considering the specification
you quoted above.  You must have some other criteria for saying it
is.  Care to share them?

> I see three ways to fix this:
>
> 1) automatically rethrow cancellation.

That's a bad idea.  At language boundaries you might need to
translate a cancellation exception into something else, then
re-generate a cancellation exception when you get back to C++.

> 2) explicitly rethrow cancellation--

That seems to violate the specification you quoted.

>    this would require some way to catch it
>    specifically, but we really ought to have that anyway.

Probably.

> 3) don't catch and swallow all exceptions; changing the catch(...) to
>    catch(std::exception) would catch all exceptions thrown by other parts
>    of the library, which AFAIK is what we're trying to accomplish.

That also seems to violate the specification you quoted.

> Thoughts?

Either you have to lobby to get the standard changed, live with the
fact that you're going to break it, or live with the fact that not
having badbit set will suppress cancellation in some circumstances.  I
don't think it's honest to "fix" the problem by calling the latter
behavior a bug if that's what the specifications say to do.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com



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