This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
catch(...) and forced unwind
- From: Jason Merrill <jason at redhat dot com>
- To: gcc at gcc dot gnu dot org
- Cc: Richard Henderson <rth at redhat dot com>, Mark Mitchell <mark at codesourcery dot com>, Ulrich Drepper <drepper at redhat dot com>, Benjamin Kosnik <bkoz at redhat dot com>, Jakub Jelinek <jakub at redhat dot com>, ncm at cantrip dot org, "Gabriel Dos Reis <gdr at codesourcery dot com>David Abrahams" <dave at boost-consulting dot com>, wekempf at cox dot net, fjh at cs dot mu dot oz dot au, Jason Merrill <jason at redhat dot com>
- Date: Fri, 12 Dec 2003 17:01:08 -0500
- Subject: 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.
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.
The current compromise behavior is to call terminate so that the user is
aware of the problem.
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.
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. 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.
I see three ways to fix this:
1) automatically rethrow cancellation.
2) explicitly rethrow cancellation--this would require some way to catch it
specifically, but we really ought to have that anyway.
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.
Thoughts?
Jason