Discussion:
Behaviour of __forced_unwind with noexcept
Ron
2017-08-13 18:20:02 UTC
Permalink
Hi,

I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.

The closest I've found so far to an "authoritative" statement of the
expected behaviour is the comments from Jonathan Wakely here:

https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex

In particular: "It interacts with noexcept as you'd expect:
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"

Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).

And since that could be something as simple as logging to stdio and
almost impossible to definitely rule out in a future proof way if the
destructor does anything non-trivial (like calling functions in a
system or other 3rd party library) ... and since the race of catching
a cancellation request in such a destructor could be a relatively rare
one to lose in a lot of code ... there could be a lot of extant code
with new latent 'crasher' bugs when built with GCC 6.1 or later.

And/or a lot of people are going to have to go through a lot of code
and mark up a lot of methods with noexcept(false).


So I'm half-hoping that I am actually missing something here which
mitigates that - but if not, is this something we need to give a
bit more thought to?

(Please keep me cc'd, I'm not currently on this list)

Cheers,
Ron
Ron
2017-08-15 04:44:13 UTC
Permalink
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?

Would it be feasible for the compiler to automatically generate that?

For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).


It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).

I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.


I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.

On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).


It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".

Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Post by Ron
And since that could be something as simple as logging to stdio and
almost impossible to definitely rule out in a future proof way if the
destructor does anything non-trivial (like calling functions in a
system or other 3rd party library) ... and since the race of catching
a cancellation request in such a destructor could be a relatively rare
one to lose in a lot of code ... there could be a lot of extant code
with new latent 'crasher' bugs when built with GCC 6.1 or later.
And/or a lot of people are going to have to go through a lot of code
and mark up a lot of methods with noexcept(false).
So I'm half-hoping that I am actually missing something here which
mitigates that - but if not, is this something we need to give a
bit more thought to?
(Please keep me cc'd, I'm not currently on this list)
Cheers,
Ron
Richard Biener
2017-08-15 10:24:20 UTC
Permalink
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?

I guess sprinkling pthread_setcancelstate () around would be doable
(in some __cxxabi helper) but it might be quite expensive to do so...

Richard.
Post by Ron
Post by Ron
And since that could be something as simple as logging to stdio and
almost impossible to definitely rule out in a future proof way if the
destructor does anything non-trivial (like calling functions in a
system or other 3rd party library) ... and since the race of catching
a cancellation request in such a destructor could be a relatively rare
one to lose in a lot of code ... there could be a lot of extant code
with new latent 'crasher' bugs when built with GCC 6.1 or later.
And/or a lot of people are going to have to go through a lot of code
and mark up a lot of methods with noexcept(false).
So I'm half-hoping that I am actually missing something here which
mitigates that - but if not, is this something we need to give a
bit more thought to?
(Please keep me cc'd, I'm not currently on this list)
Cheers,
Ron
Richard Biener
2017-08-15 11:31:10 UTC
Permalink
Post by Richard Biener
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?
If the forced_unwind exception escapes a noexcept function then the
compiler calls std::terminate(). That can be replaced by the user so
that it doesn't call abort(). It must not return, but a user-supplied
terminate handler could trap or raise SIGKILL or something else.
Required behavior: A terminate_handler shall terminate execution of
the program without returning
to the caller.
Default behavior: The implementation’s default terminate_handler calls abort().
I don't think glibc can help, I think the compiler would need to
change to not call std::terminate().
Maybe it could call an unwinder provided hook so that forced_unwind can
set it to sth stopping the unwinding and signalling an error rather than
abort()ing.

Richard.
Ron
2017-08-15 15:21:42 UTC
Permalink
Post by Richard Biener
Post by Richard Biener
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?
If the forced_unwind exception escapes a noexcept function then the
compiler calls std::terminate(). That can be replaced by the user so
that it doesn't call abort(). It must not return, but a user-supplied
terminate handler could trap or raise SIGKILL or something else.
Required behavior: A terminate_handler shall terminate execution of
the program without returning
to the caller.
Default behavior: The implementation’s default terminate_handler calls abort().
I don't think glibc can help, I think the compiler would need to
change to not call std::terminate().
Maybe it could call an unwinder provided hook so that forced_unwind can
set it to sth stopping the unwinding and signalling an error rather than
abort()ing.
The trick there is that we don't actually want to just stop the unwinding
at the first place where it hits a noexcept function (or we're back to
the situation that force_unwind was created to avoid, resources higher up
the stack may be leaked).

And my gut feeling is that we don't want to diverge from the standard as
to when a real error (an exception leaving a noexcept context) occurs.

But it seems to me that we can avoid the problem case, and stay within
the letter and spirit of the standard just by always ensuring that
pthread_cancel is never acted upon in a noexcept context. That way we
simply never throw an exception, and don't need to do anything tricky
about handling it. The cancel request will just get acted upon at the
next cancellation point which isn't in a noexcept scope. All other
errant exceptions will still behave exactly as expected.

All we'd be adding is a simple extension to noexcept, which isn't in
direct violation of the standard, to complement the stack unwinding
extension that the standard didn't account for with noexcept.


Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?

If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?

Ron
Richard Biener
2017-08-15 15:47:41 UTC
Permalink
Post by Ron
Post by Richard Biener
Post by Richard Biener
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?
If the forced_unwind exception escapes a noexcept function then the
compiler calls std::terminate(). That can be replaced by the user so
that it doesn't call abort(). It must not return, but a user-supplied
terminate handler could trap or raise SIGKILL or something else.
Required behavior: A terminate_handler shall terminate execution of
the program without returning
to the caller.
Default behavior: The implementation’s default terminate_handler calls abort().
I don't think glibc can help, I think the compiler would need to
change to not call std::terminate().
Maybe it could call an unwinder provided hook so that forced_unwind can
set it to sth stopping the unwinding and signalling an error rather than
abort()ing.
The trick there is that we don't actually want to just stop the unwinding
at the first place where it hits a noexcept function (or we're back to
the situation that force_unwind was created to avoid, resources higher up
the stack may be leaked).
And my gut feeling is that we don't want to diverge from the standard as
to when a real error (an exception leaving a noexcept context) occurs.
But it seems to me that we can avoid the problem case, and stay within
the letter and spirit of the standard just by always ensuring that
pthread_cancel is never acted upon in a noexcept context. That way we
simply never throw an exception, and don't need to do anything tricky
about handling it. The cancel request will just get acted upon at the
next cancellation point which isn't in a noexcept scope. All other
errant exceptions will still behave exactly as expected.
All we'd be adding is a simple extension to noexcept, which isn't in
direct violation of the standard, to complement the stack unwinding
extension that the standard didn't account for with noexcept.
Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?
The noexcept checking only needs to happen if an exception is thrown
while the pthread cancel state needs to be adjusted whenever we are
about to enter/exit such function.
Post by Ron
If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?
I guess that's possible.

I suppose prototyping this would be wrapping all noexcept calls in

try { pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old); call
(); } finally { pthread_setcancelstate (old, &old); }

this shouldn't be too hard to do but may interfere with things like
constexpr evaluation if done
in the wrong point in time so I suggest doing it during genericization
if that noexcept info is
still there.

Then you can measure the impact.

Richard.
Post by Ron
Ron
Szabolcs Nagy
2017-08-15 16:37:21 UTC
Permalink
Post by Richard Biener
Post by Ron
Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?
The noexcept checking only needs to happen if an exception is thrown
while the pthread cancel state needs to be adjusted whenever we are
about to enter/exit such function.
Post by Ron
If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?
I guess that's possible.
I suppose prototyping this would be wrapping all noexcept calls in
try { pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old); call
(); } finally { pthread_setcancelstate (old, &old); }
i think changing the state this way is only valid if call
itself does not change the state, which we don't know.
Ron
2017-08-15 17:49:27 UTC
Permalink
Post by Szabolcs Nagy
Post by Richard Biener
Post by Ron
Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?
The noexcept checking only needs to happen if an exception is thrown
while the pthread cancel state needs to be adjusted whenever we are
about to enter/exit such function.
Post by Ron
If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?
I guess that's possible.
I suppose prototyping this would be wrapping all noexcept calls in
try { pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old); call
(); } finally { pthread_setcancelstate (old, &old); }
i think changing the state this way is only valid if call
itself does not change the state, which we don't know.
That's a fair statement, but we're only talking about doing this in
functions which are declared noexcept.

It already requires the person writing that code to ensure that
no exceptions are thrown (else the process will terminate), and
this extension would additionally require them to ensure that no
cancellation points become active (which is already required now
anyway since that's already directly equivalent to throwing an
exception as things are now).

The only difference is the compiler can do it safely, where trying
to write that explicitly in user code will still leave gaps where
an unwind exception could leak out.

Szabolcs Nagy
2017-08-15 16:35:45 UTC
Permalink
Post by Ron
Post by Richard Biener
Post by Richard Biener
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?
If the forced_unwind exception escapes a noexcept function then the
compiler calls std::terminate(). That can be replaced by the user so
that it doesn't call abort(). It must not return, but a user-supplied
terminate handler could trap or raise SIGKILL or something else.
Required behavior: A terminate_handler shall terminate execution of
the program without returning
to the caller.
Default behavior: The implementation’s default terminate_handler calls abort().
I don't think glibc can help, I think the compiler would need to
change to not call std::terminate().
Maybe it could call an unwinder provided hook so that forced_unwind can
set it to sth stopping the unwinding and signalling an error rather than
abort()ing.
The trick there is that we don't actually want to just stop the unwinding
at the first place where it hits a noexcept function (or we're back to
the situation that force_unwind was created to avoid, resources higher up
the stack may be leaked).
And my gut feeling is that we don't want to diverge from the standard as
to when a real error (an exception leaving a noexcept context) occurs.
But it seems to me that we can avoid the problem case, and stay within
the letter and spirit of the standard just by always ensuring that
pthread_cancel is never acted upon in a noexcept context. That way we
simply never throw an exception, and don't need to do anything tricky
about handling it. The cancel request will just get acted upon at the
next cancellation point which isn't in a noexcept scope. All other
errant exceptions will still behave exactly as expected.
All we'd be adding is a simple extension to noexcept, which isn't in
direct violation of the standard, to complement the stack unwinding
extension that the standard didn't account for with noexcept.
note that there are at least two standards involved: c++ and posix
and the later is followed by libc which is not under the control
of libstdc++.

on the c++ side:
thread cancellation is undefined in c++ and i don't think it can be
made to work in general with correct dtor behaviour at least for
async cancellation.

on the libc side:
a correct libc implementation cannot rely on c++ unwind abi, so
thread cancellation must not use the c++ exception handling
mechanism for cleanup handlers (unless c++ unwind abi is part of
the target platform abi, which is not on many supported targets).

however in this regard glibc is a non-standard implementation,
it uses c++ eh so if dtors don't work then pthread_cleanup_push
will fail too (this is non-conforming as requires non-standard
cflag, -fexceptions, for c code that uses pthread_cleanup_push.
it is also broken for async cancellation, so some posix conforming
code is broken on glibc even if -fexceptions is used).

to the original stackoverflow question the answer should be
that cancelling a thread with c++ code is undefined and thus
non-portable and using pthread_cleanup_push in c++ code does
not help on glibc.
Post by Ron
Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?
If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?
Ron
Ron
2017-08-15 17:35:06 UTC
Permalink
Post by Ron
Post by Richard Biener
Post by Richard Biener
Post by Ron
Post by Ron
Hi,
I'm looking for some clarification of how the __forced_unwind thread
cancellation exceptions intersect with noexcept. I've long been a
big fan of the __forced_unwind idiom, but now that C++14 is the default
since GCC 6.1, and many methods including destructors are implicitly
noexcept, using it safely appears to have become a lot more tricky.
The closest I've found so far to an "authoritative" statement of the
https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
std::terminate() is called if a __forced_unwind escapes a noexcept
function, so noexcept functions are really noexcept, they won't
unexpectedly throw some 'special' type"
Which does seem logical, but unless I'm missing something this makes
it unsafe to perform any operation in a destructor which might cross
a cancellation point, unless that destructor is noexcept(false).
Unfortunately I still think that's true.
This was also raised in https://gcc.gnu.org/ml/gcc-help/2015-08/msg00040.html
Ouch. Had you considered the option of having any scope that is
noexcept(true) also be treated as if it was implicitly in a scoped
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE), restoring the
old state when it leaves that scope?
Would it be feasible for the compiler to automatically generate that?
For any toolchain which does use the unwinding exceptions extension,
that also seems like a logical extension to the noexcept behaviour,
since allowing cancellation will otherwise result in an exception and
process termination. If people really need cancellation in such
scopes, then they can more manageably mark just those noexcept(false).
It would need to be done by the compiler, since in user code I can't
do that in a destructor in a way that will also protect unwinding
members of a class (which may have destructors in code I don't
control).
I can't even completely mitigate this by just always using -std=c++03
because presumably I'm also exposed to (at least) libstdc++.so being
built with the new compiler default of C++14 or later.
I'd be really sad to lose the stack unwinding we currently have when
a thread is cancelled. I've always known it was an extension (and I'm
still a bit surprised it hasn't become part of the official standard),
but it is fairly portable in practice.
On Linux (or on Debian at least) clang also supports it. It's also
supported by gcc on FreeBSD and MacOS (though not by clang there).
It's supported by mingw for Windows builds. OpenBSD is currently
the only platform I know of where even its gcc toolchain doesn't
support this (but they're also missing support for standard locale
functionality so it's a special snowflake anyway).
It seems that we need to find some way past the status-quo though,
because "don't ever use pthread_cancel" is the same as saying that
there's no longer any use for the forced_unwind extension. Or that
"you can have a pthread_cancel which leaks resources, or none at all".
Having a pthread_cancel that only works on cancellation points that
aren't noexcept seems like a reasonable compromise and extension to
the shortcomings of the standard to me. Am I missing something there
which makes that solution not a viable option either?
Have glibc override the abort () from the forced_unwind if in pthread_cancel
context?
If the forced_unwind exception escapes a noexcept function then the
compiler calls std::terminate(). That can be replaced by the user so
that it doesn't call abort(). It must not return, but a user-supplied
terminate handler could trap or raise SIGKILL or something else.
Required behavior: A terminate_handler shall terminate execution of
the program without returning
to the caller.
Default behavior: The implementation’s default terminate_handler calls abort().
I don't think glibc can help, I think the compiler would need to
change to not call std::terminate().
Maybe it could call an unwinder provided hook so that forced_unwind can
set it to sth stopping the unwinding and signalling an error rather than
abort()ing.
The trick there is that we don't actually want to just stop the unwinding
at the first place where it hits a noexcept function (or we're back to
the situation that force_unwind was created to avoid, resources higher up
the stack may be leaked).
And my gut feeling is that we don't want to diverge from the standard as
to when a real error (an exception leaving a noexcept context) occurs.
But it seems to me that we can avoid the problem case, and stay within
the letter and spirit of the standard just by always ensuring that
pthread_cancel is never acted upon in a noexcept context. That way we
simply never throw an exception, and don't need to do anything tricky
about handling it. The cancel request will just get acted upon at the
next cancellation point which isn't in a noexcept scope. All other
errant exceptions will still behave exactly as expected.
void f() noexcept
{
try {
g();
} catch (...) {
throw;
}
}
The forced_unwind would be caught, at which point the stack would have
been unwound to that point, and then it would terminate. In theory we
could allow cancellation during the execution of g(). Your proposal
would prevent that.
Unless I misunderstood Richard's suggestion (of making that report an
error rather than terminating), then the problem case I saw was:

void e()
{
ScopedMutex m;
f();
}

So if we stopped unwinding at f(), but didn't terminate, then the
mutex would stay locked. Or other finalisation, like writing
things to disk wouldn't happen (whether we terminated or not).
Also currently std::thread runs the supplied function object inside a
noexcept function. With your proposal cancellation would be blocked in
any thread created by a std::thread, i.e. you could only cancel in the
main() thread, or threads created by pthread_create.
That type of thing could be problem, but the fix for it is to explicitly
declare any such function noexcept(false) if it would implicitly be a
noexcept function. Which is a lot less places to fix than doing that
to every dtor which might be a cancellation point (which you can only
do if you have change control for every dtor you might use).

Right now we have the situation where if you do cancel anything in a
scope under a noexcept function, the process will terminate. If you
really want that behaviour, you still have pthread_kill available to
you. But if you want to be able to cancel just a thread in a world
with noexcept, we either need to postpone that until you safely leave
the noexcept scope - or use something other than exceptions to do
the unwinding.

And the former seems more manageable than the latter.
Post by Ron
All we'd be adding is a simple extension to noexcept, which isn't in
direct violation of the standard, to complement the stack unwinding
extension that the standard didn't account for with noexcept.
Is changing the cancellation state really an expensive operation?
Moreso than the checking which I assume already needs to be done for
noexcept to trap errant exceptions?
Changing the cancel state would have to happen in the hot path, just
in case a cancellation happens.
Terminating only has to happen in the cold path if an exception is
thrown, during stack unwinding.
Nod. I can't say I'm happy about this having any speed penalty at all,
but having reliably working cancellation with proper stack unwinding
seems preferable to having existing previously-working code run just a
little faster at the risk of randomly exploding if the cancellation
steps on a hidden mine.

And I'm not rusted on to this as The Solution here - it's just the
least hateful option that I can so far see actually covering all of
the problem cases noexcept has opened up.

The penalty would only need to be incurred at the top level of any
noexcept scope, so an actually hot function could avoid most of the
cost with something like:

void a() noexcept
{
// pthread_setcancelstate()
for(;;) {
go_fast();
}
// pthread_setcancelstate()
}

Which I suspect most speed sensitive code would probably quite naturally
already look something like.
Post by Ron
If it really is, I guess we could also have an attribute which declares
a stronger guarantee than noexcept, to claim there are no cancellation
points in that scope, if people have something in a hot path where a few
cycles really matter to them and this protection is not actually needed.
Which could also be an automatic optimisation if the compiler is able to
prove there are no cancellation points?
Ron
Loading...