This is the mail archive of the
mailing list for the GCC project.
Re: Behaviour of __forced_unwind with noexcept
- From: Ron <ron at bitbabbler dot org>
- To: Jonathan Wakely <jwakely dot gcc at gmail dot com>
- Cc: "gcc at gcc dot gnu dot org" <gcc at gcc dot gnu dot org>
- Date: Tue, 15 Aug 2017 14:14:13 +0930
- Subject: Re: Behaviour of __forced_unwind with noexcept
- Authentication-results: sourceware.org; auth=none
- References: <20170813182002.GJ12030@hex.shelbyville.oz> <CAH6eHdRgU6GVYJ5WD4480JJ_qOD1oYzN4UC+4SDirFdp8yj=1Q@mail.gmail.com>
On Mon, Aug 14, 2017 at 06:22:39PM +0100, Jonathan Wakely wrote:
> On 13 August 2017 at 19:20, Ron wrote:
> > 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).
> 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
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?
> > 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