This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: catch(...) and forced unwind
- From: Jason Merrill <jason at redhat dot com>
- To: David Abrahams <dave at boost-consulting dot com>
- Cc: Nathan Myers <ncm at cantrip dot org>, gcc at gcc dot gnu dot org, 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>, wekempf at cox dot net, fjh at cs dot mu dot oz dot au, William Kempf <williamkempf at hotmail dot com>, Jason Merrill <jason at redhat dot com>
- Date: Tue, 16 Dec 2003 15:30:24 -0500
- Subject: Re: catch(...) and forced unwind
- References: <xypwu91ofvf.fsf@miranda.boston.redhat.com><ud6at1wvg.fsf@boost-consulting.com><xypekv9nr9u.fsf@miranda.boston.redhat.com><u65gkyhv4.fsf@boost-consulting.com><20031214035909.GE2416@tofu.dreamhost.com><xypk74xltaa.fsf@miranda.boston.redhat.com><ullpdlksl.fsf@boost-consulting.com><xyp1xr5lh5q.fsf@miranda.boston.redhat.com><usmjlhryu.fsf@boost-consulting.com>
On Tue, 16 Dec 2003 07:23:05 -0500, David Abrahams <dave@boost-consulting.com> wrote:
> Jason Merrill <jason@redhat.com> writes:
>> The text for output streams is similar. I note, though I concede that this
>> is something of a perverse reading of the standard, that although this says
>> that the functions don't rethrow, nothing precludes them from throwing
>> implementation-defined exceptions of their own.
>
> I think that's beyond perverse. The language is clear: "otherwise it
> does not throw anything". That precludes any throwing whatsoever,
> unless of course I've misread that text and paragraph 4 is only
> describing one corner of the behavior while exceptions are allowed to
> escape under other conditions.
That is my interpretation; I think that it only describes what happens when
one of the called functions throws.
>> Also, this restriction only applies to the {un,}formatted i/o functions,
>> not to any other operations.
Any response to this point?
>> I would like to loosen the passage quoted above to allow rethrowing some
>> exceptions as defined by the implementation. I don't think this change
>> would break anyone's code, since the existing guarantee is so weak.
>
> I don't think that's a safe conclusion. There's plenty of legacy code
> which acts as though there is no such thing as an exception. You can
> pretty much do all the iostream-ing you want under that assumption,
> since they are handed to you out of the box in that state. One
> obvious place output streaming might be done is in an object's
> destructor, for logging purposes. It seems entirely plausible that
> people might scatter local objects throughout throughout functions in
> order to do tracing. If thread cancellation is picked up by an output
> streaming call in a destructor during unwinding, we'll go directly to
> terminate (boom).
Yes, that's a significant issue; this would mean people need to be even
more careful about what they do in a destructor, perhaps to the point of
disabling cancellation while the destructor is running.
>>>> Actually, this is backwards -- the standard doesn't allow the C I/O
>>>> functions to throw any exceptions; see the passage above. This obviously
>>>> needs to change if we're going to do anything useful with POSIX thread
>>>> cancellation.
>>
>>> Not sure what "anything useful" means.
>>
>> If all of the cancellation points are throw(),
>
> well, then they're not cancellation points anymore, are they?
Yes, the set of cancellation points is defined by POSIX. Cancellation will
happen regardless of whether or not C++ cleanups are run. And I believe
that sometimes silently ignoring cleanups is worse than sometimes calling
terminate.
>> the cancellation exception can't propagate, so we can't run any
>> cleanups. Which isn't very useful.
>
> It's obvious that unwinding due to cancellation has to be possible.
> [I presume there's a function people can call to explicitly induce
> unwinding when the thread is cancelled?]
Yes, pthread_testcancel. A C++ program that wants (or needs) explicit
control over where cancellation will happen can
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
in the thread startup function and
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel ();
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
to allow cancellation. But as you say, this requires explicit handling and
most exception-safe code should also be thread-safe, so we want to find the
default behavior which will be most useful.
> The question is whether it's appropriate to force unwinding in this one
> particular case where users have been given license to assume there will
> be none.
I believe that is the least bad solution.
>> Has David Butenhof been involved at all? He seems to have been
>> thinking about the interaction of threads and exceptions longer than
>> anyone else.
>
> Yes, though not directly. Bill Kempf (the Boost.Threads guy)
> communicated with him and extensively read his writings on the subject
> before he vaporized. My understanding, from everything I've read, is
> that David Butenhof believes that *any* asynchronous (forced, not
> under user control) cancellation model is wrong. IIRC there's
> something like that in pthreads and he regrets it.
I think everyone agrees that asynchronous cancellation is bad and wrong.
We're only talking about synchronous (PTHREAD_CANCEL_DEFERRED) cancellation
here, whereby cancellation occurs only at certain defined cancellation
points.
> But rather than speaking for him, I should let him speak for himself.
>
> http://tinyurl.com/zg5d
>
> Plus, we can ask him questions. He's quite approachable, I've found.
>
> The one place I have to pre-emptively disagree with him is (from the
> first link):
>
> "NOBODY has any business "finalizing" (catching and not
> releasing) an exception they don't know by name. If you can't identify the
> exception, you can't know why it was raised or what it means. And if you
> don't understand what it means, you cannot possibly know that it doesn't
> need to be handled further down the stack."
>
> Not all code is exception-safe, and not all languages can handle C++
> exceptions. At the boundaries between exception-safe code and
> everything else, catch(...) is appropriate.
Perhaps, but it's not clear to me that this is a justification for the use
in iostreams.
I think it's important to understand that, contrary to Bill's post
http://gcc.gnu.org/ml/gcc-patches/2003-05/msg00211.html
the POSIX standard does not allow cancellation to be deferred again once
the request is acted upon; it states that each of the handlers are run, and
then the thread exits. Allowing a catch block to finalize a cancellation
exception would be an extension to the POSIX semantics.
That said, Butenhof's postings certainly suggest that he thinks that this
is an appropriate extension:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=3B03B13A.D4796297%40compaq.com
You cannot "catch" a cancel and continue. That would require a full
exception model, which was beyond the scope of POSIX. However,
POSIX cancellation was loosely based on an implementation that did use
exceptions, and an ideal/complete/"philosophically correct"
implementation of POSIX cancellation will be based on exceptions. On
such a system, you can use non-standard mechanisms to "finalize" a
cancellation exception and continue. For example, you should be able to
enclose your do_something_with_pthread_testcancel() in a C++
try/catch(...) block, and "return 1" from the catch.
That's almost always a bad idea, though, since cancellation is designed
to cleanly terminate a thread; you shouldn't be using it this way.
That's especially true where your function may be called in a thread
someone else created. Only the CREATOR should ever cancel a thread; and
then your code will be foiling the legitimate intent of the thread's
creator to terminate it. You should cleanup on a cancel and then allow
it to propagate.
I agree completely with this, both that catch(...) should finalize a
cancellation and that it should almost never be used that way--in
particular, that it should not be used that way in iostreams.
The other cases of forced unwind are less clear. It's pretty obvious that
finalizing a cancellation exception would simply cause the cancellation to
be deferred until the next cancellation point. But what would it mean to
finalize a pthread_exit exception? And though we could argue that a
cancellation request isn't urgent, since it is already deferred until the
next cancellation point, that's hard to justify for pthread_exit. Harder
yet for longjmp_unwind.
Does anyone have a Tru64 system with the Compaq C++ compiler installed?
Benjamin: Pending a resolution of these issues, I suggest that we work
around the problem by disabling cancellation in the iostream try blocks.
I'm playing with this now.
Jason