Recently on CodeReview.SE, I came across an answer which talks about a technique called “exception vomiting”. Apparently this trick is used to exploit that exceptions have to be implemented in a thread-safe manner independent of whether the compiler supports thread_local
variables.
I paste part of this answer below:
There's an existing technique which is not dissimilar referred to as "exception vomiting". Observe:
void f(void(*p)()) { p(); } template<typename F> void real_f(F func) { try { throw func; } catch(...) { f([] { try { throw; } catch(F func) { func(); } }); } }
This abuses the fact that the compiler must provide a thread-local stack for complex objects for use as exception storage, regardless of their support for other thread local features, and therefore enjoys much broad compiler support. The most obvious drawbacks are a) it's horrible, and b) it's limited to stack semantics.
My question is, how does that trick actually work and is it “safe”?
This technique relies on the fact that exceptions must be implemented in a thread-safe manner in order for exceptions to be usable in a multi-threaded application. Even pre C++-11 compilers supported thread safe exceptions before threads became a part of the C++ standard.
Each thread throw
/catch
exceptions independently of other threads by using thread-specific storage for storing exceptions. throw
without an argument rethrows the current exception stored in that thread-specific storage. This storage for the exception is used to store the function with its captured arguments (stateful lambda or any other callable).
The drawback of this technique is that throwing an exception normally involves a memory allocation, so it adds the overhead of new
/delete
invocations.
Another way to implement this is to use a non-portable but widely supported __thread
storage specifier. This avoid the overhead of dynamic memory allocation:
void f(void(*p)()) { // The C-style function.
p();
}
__thread void(*function)();
template<class Function>
void adapter() {
(*reinterpret_cast<Function*>(function))();
}
template<typename F>
void invoke_f(F const& func) {
function = reinterpret_cast<void(*)()>(&func);
f(adapter<F const>);
}
int main(int ac, char**) {
invoke_f([ac]{ std::cout << ac << '\n'; });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With