I generally avoid explicitly constructing a lock guard or unique lock, because generally a lock is protecting something, it doesn’t exist in isolation. I much prefer a pattern where classes have private fields for the things protected by a lock (in a nested class, if necessary to protect the, from local access) and an accessor that returns a tuple of a lock guard and a reference to each of the fields that it protects. If this accessor is marked always inline, then it adds no overhead and ensures that the lock is held for as long as the reference to the field is in scope. With structured binding (C++17), you effectively have multiple return values, so you can grab the lock and the references trivially.
So rather than throw an exception, we’d really prefer to assert-fail as soon as possible, get a coredump, and go fix the bug. So we want to wrap all our uses of mutex::lock() in a try/catch/assert.
It’s easier to just wrap them in a function declared noexcept, which will have the same behavior.
Are you sure? I thought that was the difference between noexcept and throw(). The latter requires the compiler inserts a dynamic check that an exception isn’t thrown, the former just makes it undefined behaviour to throw an exception out of the function.
According to CPPReference, an exception thrown from a noexcept function is guaranteed to call std::terminate:
noexcept is an improved version of throw(), which is deprecated in C++11. Unlike pre-C++17 throw(), noexcept will not call std::unexpected, may or may not unwind the stack, and will call std::terminate, which potentially allows the compiler to implement noexcept without the runtime overhead of throw().
Interesting. It looks as if clang emits the equivalent of a try-catch block for this, which actually makes noexcept worse than throw() in the common case. With throw(), the compiler emits more data in the lsda, which lets the C++ personality function invoke std::unexpected during unwind. With noexcept, it must generate landing-pad code. Oddly, it looks as if clang generates landing pad code in both cases and calls std::unexpected itself, even though the runtime will do that and you’ll never hit the landing pad.
I can vouch from experience that throwing from a noexcept function logs a “uncaught exception” message and calls std::terminate. At least in libc++ but I’m pretty sure it’s standard.
When an exception is thrown the runtime looks up symbols in the binary to find the handlers for each stack frame. A noexcept function has, I assume, a handler that points to a common uncaught-exception routine, or else has a flag saying to abort.
Well, that’s just mean. You made me go and read code that I wrote over 10 years ago (the C++ personality function). It turns out I slightly misremembered how filters worked, they’re slightly less clever than I thought. Presumably because the HP folks expected a later C++ standard to have more specialised handling of unexpected exceptions.
The LSDA for C++ contains an actions table that contains (for each PC range) indexes into the type table. If these indexes are negative then they indicate filters. If the exception that is thrown is not one of the ones permitted by a filter, then the personality function installs a handler. If it is, then the personality function skips this frame (or runs cleanups, if there are any). I had assumed that the personality routine would call std::unexpected directly. I presume that this was not done because std::unexpected is not allowed to return, but might not exit the program (it may tear down the current thread, pivot to a different stack, and so on) and doing so would leak the unwinder state (and the exception state, and the exception object). With the Windows / SEH model, this is not a problem, because all of the unwind state lives on the stack and so a table-driven unwinder can handle filters without needing any code for landing pads in the function.
It would be quite easy to extend the Itanium ABI with a special marker for noexcept in the action table that simply told the personality function to call std::terminate if it reaches that function for anything other than a forced unwind. The code in clang would be slightly more complicated, but even just doing it for noexcept functions calling non-noexcept functions and not implementing any try / catch blocks would probably be a win. I might prototype it at some point and see if it makes a difference. Most of my code is compiled with -fno-exceptions, so it’s not something I’ve hit much.
I generally avoid explicitly constructing a lock guard or unique lock, because generally a lock is protecting something, it doesn’t exist in isolation. I much prefer a pattern where classes have private fields for the things protected by a lock (in a nested class, if necessary to protect the, from local access) and an accessor that returns a tuple of a lock guard and a reference to each of the fields that it protects. If this accessor is marked always inline, then it adds no overhead and ensures that the lock is held for as long as the reference to the field is in scope. With structured binding (C++17), you effectively have multiple return values, so you can grab the lock and the references trivially.
It’s easier to just wrap them in a function declared
noexcept
, which will have the same behavior.Are you sure? I thought that was the difference between noexcept and throw(). The latter requires the compiler inserts a dynamic check that an exception isn’t thrown, the former just makes it undefined behaviour to throw an exception out of the function.
According to CPPReference, an exception thrown from a noexcept function is guaranteed to call std::terminate:
https://en.cppreference.com/w/cpp/language/noexcept_spec
Interesting. It looks as if clang emits the equivalent of a try-catch block for this, which actually makes noexcept worse than throw() in the common case. With throw(), the compiler emits more data in the lsda, which lets the C++ personality function invoke std::unexpected during unwind. With noexcept, it must generate landing-pad code. Oddly, it looks as if clang generates landing pad code in both cases and calls std::unexpected itself, even though the runtime will do that and you’ll never hit the landing pad.
I can vouch from experience that throwing from a noexcept function logs a “uncaught exception” message and calls std::terminate. At least in libc++ but I’m pretty sure it’s standard.
When an exception is thrown the runtime looks up symbols in the binary to find the handlers for each stack frame. A noexcept function has, I assume, a handler that points to a common uncaught-exception routine, or else has a flag saying to abort.
Well, that’s just mean. You made me go and read code that I wrote over 10 years ago (the C++ personality function). It turns out I slightly misremembered how filters worked, they’re slightly less clever than I thought. Presumably because the HP folks expected a later C++ standard to have more specialised handling of unexpected exceptions.
The LSDA for C++ contains an actions table that contains (for each PC range) indexes into the type table. If these indexes are negative then they indicate filters. If the exception that is thrown is not one of the ones permitted by a filter, then the personality function installs a handler. If it is, then the personality function skips this frame (or runs cleanups, if there are any). I had assumed that the personality routine would call
std::unexpected
directly. I presume that this was not done becausestd::unexpected
is not allowed to return, but might not exit the program (it may tear down the current thread, pivot to a different stack, and so on) and doing so would leak the unwinder state (and the exception state, and the exception object). With the Windows / SEH model, this is not a problem, because all of the unwind state lives on the stack and so a table-driven unwinder can handle filters without needing any code for landing pads in the function.It would be quite easy to extend the Itanium ABI with a special marker for noexcept in the action table that simply told the personality function to call
std::terminate
if it reaches that function for anything other than a forced unwind. The code in clang would be slightly more complicated, but even just doing it fornoexcept
functions calling non-noexcept
functions and not implementing anytry
/catch
blocks would probably be a win. I might prototype it at some point and see if it makes a difference. Most of my code is compiled with-fno-exceptions
, so it’s not something I’ve hit much.