Catching foreign exceptions in C++

587 views
Skip to first unread message

Thiago Macieira

unread,
Sep 9, 2016, 11:37:48 AM9/9/16
to std-dis...@isocpp.org
This is a subject I am not sure belongs in the C++ standard at all, but after
discussing in the cxx-abi mailing list, we feel compelled to ask.

Question 1) is it ok at all for a catch clause to catch a foreign (non-C++)
exception? That means at least catch (...) will catch them.

This is an important question because both the Portable IA-64 C++ ABI and the
Microsoft SEH are able to "throw" non-C++ exceptions and catch (...) does
catch them. So current implementations already answer "Yes" to question 1. In
fact, the Portable IA-64 C++ Exception Handling ABI is designed in such a way
that it specifically is meant to work for non-C++ too, with a possible Java
exception thrown as well as forced unwinds due to setjmp/longjmp.

What do people who work in the standard say? Should catch (...) be allowed to
catch non-C++ exceptions?

How about forced unwinds? Forced unwinds, by definition, unwind the stack and
will destroy C++ non-trivial locals. But should they be catch()able?


Question 2) if we can catch, should we have a way to detect when the exception
is foreign? And what do the std:: methods related to exception return?

GCC allows one to catch a forced unwind with

catch (abi::__forced_unwind)

In other implementations of the IA-64 C++ ABI, it may be possible to detect
such a situation in:

catch (...) {
if (std::current_exception()) {
/* it's C++ */
} else {
/* it's foreign or forced unwind */
}
}

So that brings the question: what should these functions return
std::current_exception
std::uncaught_exception
std::uncaught_exceptions



Background:

std::thread is standardised to call std::terminate() if the user's function
throws. The simplest implementation of the call to the user function would be:

try {
invoke(F);
} catch (...) {
std::terminate();
}

However, if the user function calls pthread_exit() or if the thread is killed
by pthread_kill(), then some implementations will perform a forced unwind so
that pthread_cleanup handlers are run. That causes a non-local exit from the
user function and ends up in that catch (...) block above.

Conclusion: pthread_exit and pthread_kill of a std::thread cause the entire
program to terminate.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Andrey Semashev

unread,
Sep 9, 2016, 12:57:15 PM9/9/16
to std-dis...@isocpp.org
On 09/09/16 18:37, Thiago Macieira wrote:
> This is a subject I am not sure belongs in the C++ standard at all, but after
> discussing in the cxx-abi mailing list, we feel compelled to ask.
>
> Question 1) is it ok at all for a catch clause to catch a foreign (non-C++)
> exception? That means at least catch (...) will catch them.
>
> This is an important question because both the Portable IA-64 C++ ABI and the
> Microsoft SEH are able to "throw" non-C++ exceptions and catch (...) does
> catch them.

AFAIR MSVC does not catch SEH unless you specifically request the
compiler with a switch. It used to catch SEH in catch (...) by default
before and that was changed at some point. If you ask me, the change was
for the better - you never want to catch a SEH in a language catch block.

I don't know about the Portable IA-64 ABI.

> What do people who work in the standard say? Should catch (...) be allowed to
> catch non-C++ exceptions?

As for me - please no-no-no. As long as I'm using try/catch I expect to
handle only C++ exceptions there. I never want to handle system errors
like access violations or divide by zero there. Any non-C++ language
exceptions, given that these exceptions are not also C++ exceptions,
also fall into that category because I can't do anything about them in
the code that is not explicitly aware of that other unwinding mechanism
specifics.

IMHO, from the language standpoint, the whole program is written in C++,
so anything else is UB, and this includes any other language
implementation that may be invoked by a C++ program. If that other
language leaks into C++ then that should be UB as far as the standard is
concerned.

> How about forced unwinds? Forced unwinds, by definition, unwind the stack and
> will destroy C++ non-trivial locals. But should they be catch()able?

Is there an example in the language where such unwind is required?

> Background:
>
> std::thread is standardised to call std::terminate() if the user's function
> throws. The simplest implementation of the call to the user function would be:
>
> try {
> invoke(F);
> } catch (...) {
> std::terminate();
> }
>
> However, if the user function calls pthread_exit() or if the thread is killed
> by pthread_kill(), then some implementations will perform a forced unwind so
> that pthread_cleanup handlers are run. That causes a non-local exit from the
> user function and ends up in that catch (...) block above.

I think the language describes behavior of the program only if it
behaves according to the standard. If the program does something fishy
then all bets are off.

> Conclusion: pthread_exit and pthread_kill of a std::thread cause the entire
> program to terminate.

pthread_kill delivers a signal to the thread. It does not cause stack
unwind unless the signal handler initiates one. Whether that is possible
or not depends on the system and is not standardized by POSIX or C++,
AFAIK. Basically that means that killing a thread is UB in C++.

pthread_cleanup handlers are not required to be implemented in the same
manner as C++ objects. The handlers are not required to be called when
the stack unwinds due to a C++ exception. Similarly, you cannot expect
C++ automatic destructors to be called when you call pthread_exit or
pthread_cancel. Therefore these functions are also off limits in C++.

You could propose std::this_thread::exit() or something like that and
define its behavior in terms of the language. But for now the only two
valid ways of terminating a thread are a normal return and a C++
exception (aside from std::terminate() and other functions that also
terminate the whole process). IMHO, mixing something not defined by the
language into this system is a bad idea because it would make writing
portable code next to impossible.

Richard Smith

unread,
Sep 9, 2016, 2:17:05 PM9/9/16
to std-dis...@isocpp.org
On Fri, Sep 9, 2016 at 8:37 AM, Thiago Macieira <thi...@macieira.org> wrote:
This is a subject I am not sure belongs in the C++ standard at all, but after
discussing in the cxx-abi mailing list, we feel compelled to ask.

Question 1) is it ok at all for a catch clause to catch a foreign (non-C++)
exception? That means at least catch (...) will catch them.

This is an important question because both the Portable IA-64 C++ ABI and the
Microsoft SEH are able to "throw" non-C++ exceptions and catch (...) does
catch them. So current implementations already answer "Yes" to question 1. In
fact, the Portable IA-64 C++ Exception Handling ABI is designed in such a way
that it specifically is meant to work for non-C++ too, with a possible Java
exception thrown as well as forced unwinds due to setjmp/longjmp.

What do people who work in the standard say? Should catch (...) be allowed to
catch non-C++ exceptions?

I think there are two reasonable choices here: either the exception pretends to be a C++ exception for the purpose of C++ code (therefore: it triggers destructors to run, catch (...) blocks to execute, it can be captured by current_exception and rethrown, it is counted against the uncaught_exceptions count, it can be swallowed, throw; rethrows it, and if it's thrown from a noexcept function you get a call to std::terminate) -- or it does not pretend to be a C++ exception, and you don't get any C++ stack unwinding for it at all (it does not cause destructors to run nor catch (...) to be entered and can happen even within a noexcept function).

I'm not seeing any defensible middle ground, at least not if we don't want some notion of "foreign exception safety" in addition to our existing notion of exception safety -- it is not reasonable to expect code even strongly exception safe code to be prepared for the case where destructors run but catch(...) does not, nor for the case where catch(...) is entered without an active exception, nor for the case where destructors run during stack unwinding but uncaught_exceptions is zero.

Which option you pick would depend on the kind of exception and the situation. For instance, if the implementation supports mixing, say, C# and C++ code in the same process, it may well be reasonable for C# exceptions to pretend to be C++ exceptions when in C++ code, and perhaps vice versa. Conversely, for cases like SEH, implementing longjmp via exceptions, ... you probably don't want stack unwinding of any kind at the level of C++ semantics.

How about forced unwinds? Forced unwinds, by definition, unwind the stack and
will destroy C++ non-trivial locals. But should they be catch()able?

Some implementations would need to generate additional code (or turn off certain exception handling optimizations) in order to allow the unwinder to run only destructors and not catch(...) blocks. That seems an unreasonable penalty to impose to support this feature to me.

If we model this as a completely normal, swallowable exception, it doesn't seem like the model is broken -- in the pthread_cancel case, presumably the next time the thread reaches a cancellation point it will again throw the cancellation exception, which I think is a reasonable programming model.

Question 2) if we can catch, should we have a way to detect when the exception
is foreign?

That would seem reasonable, yes, but probably belongs in the ABI specification rather than in the standard. Perhaps this could be modelled as a custom base class for foreign exceptions, or perhaps as a predicate over exception_ptr, depending on the needs of the ABI.
 
--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

FrankHB1989

unread,
Sep 9, 2016, 2:19:53 PM9/9/16
to ISO C++ Standard - Discussion


在 2016年9月10日星期六 UTC+8上午12:57:15,Andrey Semashev写道:
On 09/09/16 18:37, Thiago Macieira wrote:
> This is a subject I am not sure belongs in the C++ standard at all, but after
> discussing in the cxx-abi mailing list, we feel compelled to ask.
>
> Question 1) is it ok at all for a catch clause to catch a foreign (non-C++)
> exception? That means at least catch (...) will catch them.
>
> This is an important question because both the Portable IA-64 C++ ABI and the
> Microsoft SEH are able to "throw" non-C++ exceptions and catch (...) does
> catch them.

AFAIR MSVC does not catch SEH unless you specifically request the
compiler with a switch. It used to catch SEH in catch (...) by default
before and that was changed at some point. If you ask me, the change was
for the better - you never want to catch a SEH in a language catch block.

IIRC, it changed since VC8, when there no /EHa specified.
 
I don't know about the Portable IA-64 ABI.

> What do people who work in the standard say? Should catch (...) be allowed to
> catch non-C++ exceptions?

As for me - please no-no-no. As long as I'm using try/catch I expect to
handle only C++ exceptions there. I never want to handle system errors
like access violations or divide by zero there. Any non-C++ language
exceptions, given that these exceptions are not also C++ exceptions,
also fall into that category because I can't do anything about them in
the code that is not explicitly aware of that other unwinding mechanism
specifics.

IMHO, from the language standpoint, the whole program is written in C++,
so anything else is UB, and this includes any other language
implementation that may be invoked by a C++ program. If that other
language leaks into C++ then that should be UB as far as the standard is
concerned.
I don't think so. ISO C++ is not only served to pure C++ programs, with "C" mandated language linkage (and possible with other implementation-defined language linkages) to support:

N4606
7.5/3 Every implementation shall provide for linkage to functions written in the C programming language, "C",
and linkage to C++ functions, "C++". [ Example:
complex sqrt(complex); // C++ linkage by default
extern "C" {
double sqrt(double); // C linkage
}
—end example ]

Since ISO C does not specify foreign exceptions at all, as per WG14 N1570 Clause 4, it is effectively UB for this topic. But treating arbitrary C++ programs linked with C functions having UB is too aggressive.

There are other cases still subtle, e.g. aliasing headed by `reinterpret_cast` between `wchar_t*` and `char16_t*`, then passing the cast result as a parameter to be accessed across call frames. If both caller and callee are C++ functions, this might violate strict aliasing rule, thus UB. However, this should not be refer as a case of UB (in sense of both ISO C and ISO C++) when the callee is implemented by C and `wchar_t` is same to `char16_t` in that C implementation.
 

Andrey Semashev

unread,
Sep 9, 2016, 2:47:04 PM9/9/16
to std-dis...@isocpp.org
On 09/09/16 21:19, FrankHB1989 wrote:
>
> 在 2016年9月10日星期六 UTC+8上午12:57:15,Andrey Semashev写道:
>
> IMHO, from the language standpoint, the whole program is written in
> C++,
> so anything else is UB, and this includes any other language
> implementation that may be invoked by a C++ program. If that other
> language leaks into C++ then that should be UB as far as the
> standard is
> concerned.
>
> I don't think so. ISO C++ is not only served to pure C++ programs, with
> "C" mandated language linkage (and possible with other
> implementation-defined language linkages) to support:
>
> N4606
> 7.5/3 Every implementation shall provide for linkage to functions
> written in the C programming language, "C",
> and linkage to C++ functions, "C++". [ Example:
> complex sqrt(complex); // C++ linkage by default
> extern "C" {
> double sqrt(double); // C linkage
> }
> —end example ]
>
> Since ISO C does not specify foreign exceptions at all, as per WG14
> N1570 Clause 4, it is effectively UB for this topic. But treating
> arbitrary C++ programs linked with C functions having UB is too aggressive.

Yes, my wording was unnecessarilly broad. What I was trying to say is
that if a foreign exception propagates into a C++ code then that should
be treated as UB by the C++ standard because the standard does not
describe this mechanism. The same works the other way around - portable
C programs do not expect exceptions and not required to support stack
unwinding at all. The set of interactions between different languages
allowed by the C++ standard is limited to calling functions with linkage
other than "C++" (of which only "C" is mentioned).

Thiago Macieira

unread,
Sep 9, 2016, 6:07:58 PM9/9/16
to std-dis...@isocpp.org
[cut the part about SEH and catch, as I'm actually in agreement that
catch(...) catching divisions by zero is rather inconvenient]

On sexta-feira, 9 de setembro de 2016 19:57:11 PDT Andrey Semashev wrote:
> > How about forced unwinds? Forced unwinds, by definition, unwind the stack
> > and will destroy C++ non-trivial locals. But should they be catch()able?
>
> Is there an example in the language where such unwind is required?

C++ with POSIX cancellation points implemented by way of forced unwinding.

> > Background:
> >
> > std::thread is standardised to call std::terminate() if the user's
> > function
> > throws. The simplest implementation of the call to the user function would
be:
> > try {
> > invoke(F);
> > } catch (...) {
> > std::terminate();
> > }
> >
> > However, if the user function calls pthread_exit() or if the thread is
> > killed by pthread_kill(), then some implementations will perform a forced
> > unwind so that pthread_cleanup handlers are run. That causes a non-local
> > exit from the user function and ends up in that catch (...) block above.
>
> I think the language describes behavior of the program only if it
> behaves according to the standard. If the program does something fishy
> then all bets are off.

What's fishy about the description? Note the correction below.

> > Conclusion: pthread_exit and pthread_kill of a std::thread cause the
> > entire
> > program to terminate.
>
> pthread_kill delivers a signal to the thread. It does not cause stack
> unwind unless the signal handler initiates one. Whether that is possible
> or not depends on the system and is not standardized by POSIX or C++,
> AFAIK. Basically that means that killing a thread is UB in C++.

Oops, I misspoke. I meant pthread_cancel, not pthread_kill. That is documented
with:

"[...]. When the cancellation is acted on, the cancellation cleanup handlers
for thread shall be called." (see http://pubs.opengroup.org/onlinepubs/
9699919799/functions/pthread_cancel.html)

There's nothing fishy or undefined about pthread_cancel or pthread_exit.

Moreover, because std::thread exposes the native handle, it stands to reason
that the C++ standard intends for its std::thread class to interoperate
cleanly with the underlying threading functions. That means pthread_cancel and
pthread_exit must be honoured.

The problem is that POSIX does not say what should happen to C++ non-trivial
objects when the thread is cancelled and the C++ standard does not discuss
POSIX thread cancellation.

> pthread_cleanup handlers are not required to be implemented in the same
> manner as C++ objects. The handlers are not required to be called when
> the stack unwinds due to a C++ exception. Similarly, you cannot expect
> C++ automatic destructors to be called when you call pthread_exit or
> pthread_cancel. Therefore these functions are also off limits in C++.

I agree with everything except for the last sentence. Like you and I said,
neither standard (C++ or POSIX) describes what happens when you combine the
two things together.

> You could propose std::this_thread::exit() or something like that and
> define its behavior in terms of the language. But for now the only two
> valid ways of terminating a thread are a normal return and a C++
> exception (aside from std::terminate() and other functions that also
> terminate the whole process). IMHO, mixing something not defined by the
> language into this system is a bad idea because it would make writing
> portable code next to impossible.

I disagree. Like I said above, the fact that std::thread exposes the
underlying native handle implies that the C++ class is intended to
interoperate with the underlying API.

That's different from QThread: it doesn't expose the pthread_t handle. If you
called a pthread function in a thread created by QThread and it misbehaved, I
wouldn't accept the bug report and push back "well, yeah, that's your fault".

Andrey Semashev

unread,
Sep 10, 2016, 5:31:37 AM9/10/16
to std-dis...@isocpp.org
On 09/10/16 01:07, Thiago Macieira wrote:
>>
>> Is there an example in the language where such unwind is required?
>
> C++ with POSIX cancellation points implemented by way of forced unwinding.

That's beyond the C++ language.

>>> However, if the user function calls pthread_exit() or if the thread is
>>> killed by pthread_kill(), then some implementations will perform a forced
>>> unwind so that pthread_cleanup handlers are run. That causes a non-local
>>> exit from the user function and ends up in that catch (...) block above.
>>
>> I think the language describes behavior of the program only if it
>> behaves according to the standard. If the program does something fishy
>> then all bets are off.
>
> What's fishy about the description? Note the correction below.

[snip]

> There's nothing fishy or undefined about pthread_cancel or pthread_exit.

The standard does not describe the concept of thread cancellation, which
means that this operation is not C++ and as such is not bound to follow
the behavior of the standard machine set by the C++ standard. In
practice it means no C++ stack unwinding. That's what I call fishy.

> Moreover, because std::thread exposes the native handle, it stands to reason
> that the C++ standard intends for its std::thread class to interoperate
> cleanly with the underlying threading functions. That means pthread_cancel and
> pthread_exit must be honoured.

The native handles are left as backdoors for some native APIs that are
not covered by the standard (yet?) but may still be useful. It doesn't
automatically make all those APIs compatible with C++, and I don't think
it intends to. That would be unrealistic.

> The problem is that POSIX does not say what should happen to C++ non-trivial
> objects when the thread is cancelled and the C++ standard does not discuss
> POSIX thread cancellation.

Thing is POSIX doesn't know about C++, it only describes interfaces in
C. C++ may know about POSIX but it certainly tries not to because not
all target systems are POSIX.

The only way I see to solve this is to describe the intended behavior in
the C++ standard, but that requires the behavior to be (a) sane in the
C++ world and (b) portable beyond POSIX. I wouldn't say that the current
definition of pthread_cancel or pthread_exit fits (a) - noone wants to
cancel a thread without running the destructors first.

Realistically, I don't think pthread_cancel or pthread_exit will get any
better position in C++. The functions are standardized by POSIX and have
stable implementations, so it's unlikely they will change in favor of
C++. At the same time their behavior is incompatible with C++, so I
don't think it is reasonable to describe them in the standard. Your best
bet is to propose a C++ alternative. I think the closest thing there is
now in C++ is thread interruption in Boost.Thread. It comes with a cost,
and I suspect this was one of the reasons that it didn't get into the
standard.

>> pthread_cleanup handlers are not required to be implemented in the same
>> manner as C++ objects. The handlers are not required to be called when
>> the stack unwinds due to a C++ exception. Similarly, you cannot expect
>> C++ automatic destructors to be called when you call pthread_exit or
>> pthread_cancel. Therefore these functions are also off limits in C++.
>
> I agree with everything except for the last sentence. Like you and I said,
> neither standard (C++ or POSIX) describes what happens when you combine the
> two things together.

Exactly, that's what makes it off limits.

Thiago Macieira

unread,
Sep 10, 2016, 3:01:45 PM9/10/16
to std-dis...@isocpp.org
On sábado, 10 de setembro de 2016 12:31:34 PDT Andrey Semashev wrote:
> On 09/10/16 01:07, Thiago Macieira wrote:
> >> Is there an example in the language where such unwind is required?
> >
> > C++ with POSIX cancellation points implemented by way of forced unwinding.
>
> That's beyond the C++ language.

Indeed. But it's still relevant.

> > Moreover, because std::thread exposes the native handle, it stands to
> > reason that the C++ standard intends for its std::thread class to
> > interoperate cleanly with the underlying threading functions. That means
> > pthread_cancel and pthread_exit must be honoured.
>
> The native handles are left as backdoors for some native APIs that are
> not covered by the standard (yet?) but may still be useful. It doesn't
> automatically make all those APIs compatible with C++, and I don't think
> it intends to. That would be unrealistic.

That's a self-contradicting argument. You can't argue that the handle is
exposed so that people can do things the standard doesn't (yet) describe, then
disallow use of certain API because the standard doesn't describe it.

I maintain that exposing the handle means I should be able to use any function
from the underlying API. If I'm not meant to do it, then the native handle
should be deprecated and removed.

At the very least, it should be QoI and implementation-defined behaviour to use
the underlying API with std::thread. That may make this entire discussion
implementation-specific.

> The only way I see to solve this is to describe the intended behavior in
> the C++ standard, but that requires the behavior to be (a) sane in the
> C++ world and (b) portable beyond POSIX. I wouldn't say that the current
> definition of pthread_cancel or pthread_exit fits (a) - noone wants to
> cancel a thread without running the destructors first.

Agreed.

> Realistically, I don't think pthread_cancel or pthread_exit will get any
> better position in C++. The functions are standardized by POSIX and have
> stable implementations, so it's unlikely they will change in favor of
> C++. At the same time their behavior is incompatible with C++, so I
> don't think it is reasonable to describe them in the standard. Your best
> bet is to propose a C++ alternative. I think the closest thing there is
> now in C++ is thread interruption in Boost.Thread. It comes with a cost,
> and I suspect this was one of the reasons that it didn't get into the
> standard.

There's a very clean way of integrating them. GCC and Glibc have long ago
solved this problem by equating POSIX thread-cancellation points and C++
exceptions. All POSIX thread-cancellation functions are not noexcept, whereas
all others are. Thread cancellation runs C++ local destructors and
pthread_cleanup is implemented in C++ by way of a local variable with
destructor.. This means C++ destructors are run when a cancellation happens
and that POSIX cleanups are run when an exception is thrown. Suddenly, with no
change whatsoever, locker objects like std::lock_guard, std::unique_lock,
QMutexLocker, etc. work just fine and will unlock their objects in case of
cancellation.

I would call this very much compatible with C++. The only difference between a
POSIX thread cancellation and an exception is that you cannot stop the
cancellation. You're allowed to clean up in your catch block, but you must
rethrow.

It's difficult to standardise that because, like you said, POSIX doesn't deal
with C++ and C++ works outside of POSIX. I would actually argue that POSIX
should deal with this, but there may be things C++ could do to make it easier.
Hence this thread.

> >> pthread_cleanup handlers are not required to be implemented in the same
> >> manner as C++ objects. The handlers are not required to be called when
> >> the stack unwinds due to a C++ exception. Similarly, you cannot expect
> >> C++ automatic destructors to be called when you call pthread_exit or
> >> pthread_cancel. Therefore these functions are also off limits in C++.
> >
> > I agree with everything except for the last sentence. Like you and I said,
> > neither standard (C++ or POSIX) describes what happens when you combine
> > the
> > two things together.
>
> Exactly, that's what makes it off limits.

Again, I disagree. Just because there's an interface area that neither
standard dealt with doesn't mean it should never be dealt with. Especially
when there's prior art showing it can be dealt with, cleanly.

Now, this may be in POSIX's camp or it may be entirely up to the
implementation (QoI). Still, the questions I asked bear answering: what does
the group of people most familiar with C++ think the answer should be?

Ville Voutilainen

unread,
Sep 10, 2016, 3:35:11 PM9/10/16
to std-dis...@isocpp.org
On 10 September 2016 at 22:01, Thiago Macieira <thi...@macieira.org> wrote:
>> The native handles are left as backdoors for some native APIs that are
>> not covered by the standard (yet?) but may still be useful. It doesn't
>> automatically make all those APIs compatible with C++, and I don't think
>> it intends to. That would be unrealistic.
> That's a self-contradicting argument. You can't argue that the handle is
> exposed so that people can do things the standard doesn't (yet) describe, then
> disallow use of certain API because the standard doesn't describe it.

I don't think that is a self-contradicting argument, because the
"doesn't automatically
make all those APIs compatible with C++" seems very sensible to me.

> I maintain that exposing the handle means I should be able to use any function
> from the underlying API. If I'm not meant to do it, then the native handle

That seems like a very liberal reading of the intent of native handles.

> should be deprecated and removed.

..so that *no* underlying functionality can ever be accessed? I fail
to agree with that.

> At the very least, it should be QoI and implementation-defined behaviour to use
> the underlying API with std::thread. That may make this entire discussion
> implementation-specific.

Native handles are already implementation-defined, per [thread.req.native]/1:

"Several classes described in this Clause have members
native_handle_type and native_handle. The
presence of these members and their semantics is implementation-defined."

Thiago Macieira

unread,
Sep 10, 2016, 11:48:38 PM9/10/16
to std-dis...@isocpp.org
On sábado, 10 de setembro de 2016 22:35:09 PDT Ville Voutilainen wrote:
> > At the very least, it should be QoI and implementation-defined behaviour
> > to use the underlying API with std::thread. That may make this entire
> > discussion implementation-specific.
>
> Native handles are already implementation-defined, per
> [thread.req.native]/1:
>
> "Several classes described in this Clause have members
> native_handle_type and native_handle. The
> presence of these members and their semantics is implementation-defined."

Fair enough.

Are we concluding that this is all implementation-specific and the standard has
no interest in helping code be portable in identifying a foreign exception or
forced unwind?

Like Richard said:
> but probably belongs in the ABI
> specification rather than in the standard. Perhaps this could be modelled
> as a custom base class for foreign exceptions, or perhaps as a predicate
> over exception_ptr, depending on the needs of the ABI.

Ville Voutilainen

unread,
Sep 11, 2016, 3:39:48 AM9/11/16
to std-dis...@isocpp.org
On 11 September 2016 at 06:48, Thiago Macieira <thi...@macieira.org> wrote:
>> Native handles are already implementation-defined, per
>> [thread.req.native]/1:
>>
>> "Several classes described in this Clause have members
>> native_handle_type and native_handle. The
>> presence of these members and their semantics is implementation-defined."
>
> Fair enough.
>
> Are we concluding that this is all implementation-specific and the standard has
> no interest in helping code be portable in identifying a foreign exception or
> forced unwind?

No, I think that would be a hasty conclusion.

> Like Richard said:
>> but probably belongs in the ABI
>> specification rather than in the standard. Perhaps this could be modelled
>> as a custom base class for foreign exceptions, or perhaps as a predicate
>> over exception_ptr, depending on the needs of the ABI.


Perhaps it could, but I need a proposal to discuss it sanely.

Tony V E

unread,
Sep 11, 2016, 12:29:28 PM9/11/16
to Thiago Macieira
> The only difference between a POSIX thread cancellation and an exception is that you cannot stop the cancellation. You're allowed to clean up in your catch block, but you must 
rethrow.

An exception that must be rethrown could be useful elsewhere‎, like contract violations maybe. 

Might also be excess complications. 



Sent from my BlackBerry portable Babbage Device
  Original Message  
From: Thiago Macieira
Sent: Saturday, September 10, 2016 3:01 PM
To: std-dis...@isocpp.org
Reply To: std-dis...@isocpp.org
Subject: Re: [std-discussion] Catching foreign exceptions in C++
--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.

Giovanni Piero Deretta

unread,
Sep 12, 2016, 5:24:55 AM9/12/16
to ISO C++ Standard - Discussion
On Sunday, September 11, 2016 at 5:29:28 PM UTC+1, Tony VE wrote:
> The only difference between a POSIX thread cancellation and an exception is that you cannot stop the cancellation. You're allowed to clean up in your catch block, but you must 
rethrow.

An exception that must be rethrown could be useful elsewhere‎, like contract violations maybe. 

Might also be excess complications. 

Such an exception can be simply implemented with a type that throws a copy of itself in its destructor. IIRC this is exactly how gcc+glibc implements posix thread cancellation.

In fact there is nothing special about POSIX synchronous cancellation; although POSIX never managed to standardize anything than C bindings, in the original author intention it was always meant to map to language exceptions. I believe this is how it is actually implemented in most unices.

Asychronous cancellation on the other hand is tricky as it can happen at any instruction boundary, making writing exception safe code extremely hard. Basically any async-cancel-safe function must be non-allocating and side effect free.

-- gpd

Viacheslav Usov

unread,
Sep 12, 2016, 7:51:08 AM9/12/16
to std-dis...@isocpp.org
On Fri, Sep 9, 2016 at 5:37 PM, Thiago Macieira <thi...@macieira.org> wrote:

> Question 1) is it ok at all for a catch clause to catch a foreign (non-C++) exception? That means at least catch (...) will catch them.

I do not think the C++ standard can refer to a "foreign exception" except as "implementation-defined behaviour". But then, does it even need to refer to them at all? If they are implementation-defined, then your question should be answered by the implementation. And the standard already says that calling anything with language linkage different from "C" and "C++" is implementation-defined.

But perhaps this is because your question is too general. Observe that, in your backgrounder, the functions you mention have the "C" language linkage. This reminds me of a question I asked here a while ago [1], which did not trigger any discussion back then.

So I think you should narrow your problem down to the "C" linkage and more specifically to exceptions thrown over the C/C++ linkage barrier, not just general "foreign exceptions".

Cheers,
V.


Thiago Macieira

unread,
Sep 12, 2016, 11:34:58 AM9/12/16
to std-dis...@isocpp.org, Giovanni Piero Deretta
On segunda-feira, 12 de setembro de 2016 02:24:55 PDT Giovanni Piero Deretta
wrote:
> Such an exception can be simply implemented with a type that throws a copy
> of itself in its destructor. IIRC this is exactly how gcc+glibc implements
> posix thread cancellation.

No, it isn't. POSIX thread cancellation is implemented by a forced unwind
implemented by the low-level unwind library, not via exceptions. The GCC
personality routine for C++ makes it look like a C++ exception to C++ code,
but it isn't actually that. And if you fail to rethrow, then it will terminate
the application.

> In fact there is nothing special about POSIX synchronous cancellation;
> although POSIX never managed to standardize anything than C bindings, in
> the original author intention it was always meant to map to language
> exceptions. I believe this is how it is actually implemented in most
> unices.

I can't confirm that on any other Unix I have access to. It seems to be the
exception, not the rule.

> Asychronous cancellation on the other hand is tricky as it can happen at
> any instruction boundary, making writing exception safe code extremely
> hard. Basically any async-cancel-safe function must be non-allocating and
> side effect free.

Right. That's a whole other problem...

Giovanni Piero Deretta

unread,
Sep 12, 2016, 12:50:09 PM9/12/16
to ISO C++ Standard - Discussion, gpde...@gmail.com
On Monday, September 12, 2016 at 4:34:58 PM UTC+1, Thiago Macieira wrote:
On segunda-feira, 12 de setembro de 2016 02:24:55 PDT Giovanni Piero Deretta
wrote:
> Such an exception can be simply implemented with a type that throws a copy
> of itself in its destructor. IIRC this is exactly how gcc+glibc implements
> posix thread cancellation.

No, it isn't. POSIX thread cancellation is implemented by a forced unwind
implemented by the low-level unwind library, not via exceptions. The GCC
personality routine for C++ makes it look like a C++ exception to C++ code,
but it isn't actually that. And if you fail to rethrow, then it will terminate
the application.

well, there really is an _cxxabiv1::__forced_unwind object, which although not fully implemented in C++, it is for all intent and purposes a C++ exception. But you are right, it doesn't rethrow from its destructor, instead the destructor calls std::terminate (more precisely the vtable is directly set up to point to terminate). The same behavior can be completely implemented in C++.
 

Thiago Macieira

unread,
Sep 12, 2016, 1:43:06 PM9/12/16
to std-dis...@isocpp.org
On segunda-feira, 12 de setembro de 2016 09:50:09 PDT Giovanni Piero Deretta
wrote:
> well, there really is an _cxxabiv1::__forced_unwind object, which although
> not fully implemented in C++, it is for all intent and purposes a C++
> exception. But you are right, it doesn't rethrow from its destructor,
> instead the destructor calls std::terminate (more precisely the vtable is
> directly set up to point to terminate). The same behavior can be completely
> implemented in C++.

Right. It's just that it wasn't, because there was no actual throw. The magic
comes from this bit of code in the personality routine, which also reveals the
way foreign exceptions are tagged[1]:

// During forced unwinding, match a magic exception type.
if (actions & _UA_FORCE_UNWIND)
{
throw_type = &typeid(abi::__forced_unwind);
}
// With a foreign exception class, there's no exception type.
// ??? What to do about GNU Java and GNU Ada exceptions?
else if (foreign_exception)
{
throw_type = &typeid(abi::__foreign_exception);
}

[1] https://code.woboq.org/gcc/libstdc++-v3/libsupc++/
eh_personality.cc.html#555
Reply all
Reply to author
Forward
0 new messages